@treelocator/runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/.eslintrc +3 -0
  2. package/.turbo/turbo-build.log +30 -0
  3. package/.turbo/turbo-test.log +18 -0
  4. package/.turbo/turbo-ts.log +4 -0
  5. package/LICENSE +22 -0
  6. package/babel.config.js +14 -0
  7. package/dist/_generated_styles.d.ts +2 -0
  8. package/dist/_generated_styles.js +1734 -0
  9. package/dist/_generated_tree_icon.d.ts +2 -0
  10. package/dist/_generated_tree_icon.js +2 -0
  11. package/dist/adapters/HtmlElementTreeNode.d.ts +16 -0
  12. package/dist/adapters/HtmlElementTreeNode.js +43 -0
  13. package/dist/adapters/adapterApi.d.ts +30 -0
  14. package/dist/adapters/adapterApi.js +1 -0
  15. package/dist/adapters/createTreeNode.d.ts +2 -0
  16. package/dist/adapters/createTreeNode.js +17 -0
  17. package/dist/adapters/getElementInfo.d.ts +2 -0
  18. package/dist/adapters/getElementInfo.js +19 -0
  19. package/dist/adapters/getParentsPath.d.ts +2 -0
  20. package/dist/adapters/getParentsPath.js +35 -0
  21. package/dist/adapters/getTree.d.ts +1 -0
  22. package/dist/adapters/getTree.js +35 -0
  23. package/dist/adapters/goUpByTheTree.d.ts +7 -0
  24. package/dist/adapters/goUpByTheTree.js +22 -0
  25. package/dist/adapters/jsx/getExpressionData.d.ts +2 -0
  26. package/dist/adapters/jsx/getExpressionData.js +44 -0
  27. package/dist/adapters/jsx/getJSXComponentBoundingBox.d.ts +5 -0
  28. package/dist/adapters/jsx/getJSXComponentBoundingBox.js +46 -0
  29. package/dist/adapters/jsx/jsxAdapter.d.ts +11 -0
  30. package/dist/adapters/jsx/jsxAdapter.js +208 -0
  31. package/dist/adapters/jsx/runtimeStore.d.ts +10 -0
  32. package/dist/adapters/jsx/runtimeStore.js +87 -0
  33. package/dist/adapters/react/fiberToSimple.d.ts +3 -0
  34. package/dist/adapters/react/fiberToSimple.js +55 -0
  35. package/dist/adapters/react/findDebugSource.d.ts +5 -0
  36. package/dist/adapters/react/findDebugSource.js +13 -0
  37. package/dist/adapters/react/findFiberByHtmlElement.d.ts +2 -0
  38. package/dist/adapters/react/findFiberByHtmlElement.js +22 -0
  39. package/dist/adapters/react/gatherFiberRoots.d.ts +2 -0
  40. package/dist/adapters/react/gatherFiberRoots.js +29 -0
  41. package/dist/adapters/react/getAllFiberChildren.d.ts +2 -0
  42. package/dist/adapters/react/getAllFiberChildren.js +9 -0
  43. package/dist/adapters/react/getAllParentsElementsAndRootComponent.d.ts +8 -0
  44. package/dist/adapters/react/getAllParentsElementsAndRootComponent.js +34 -0
  45. package/dist/adapters/react/getAllWrappingParents.d.ts +2 -0
  46. package/dist/adapters/react/getAllWrappingParents.js +19 -0
  47. package/dist/adapters/react/getFiberComponentBoundingBox.d.ts +3 -0
  48. package/dist/adapters/react/getFiberComponentBoundingBox.js +27 -0
  49. package/dist/adapters/react/getFiberLabel.d.ts +3 -0
  50. package/dist/adapters/react/getFiberLabel.js +14 -0
  51. package/dist/adapters/react/getFiberOwnBoundingBox.d.ts +3 -0
  52. package/dist/adapters/react/getFiberOwnBoundingBox.js +6 -0
  53. package/dist/adapters/react/isStyled.d.ts +2 -0
  54. package/dist/adapters/react/isStyled.js +3 -0
  55. package/dist/adapters/react/makeFiberId.d.ts +2 -0
  56. package/dist/adapters/react/makeFiberId.js +16 -0
  57. package/dist/adapters/react/reactAdapter.d.ts +11 -0
  58. package/dist/adapters/react/reactAdapter.js +114 -0
  59. package/dist/adapters/react/searchDevtoolsRenderersForClosestTarget.d.ts +1 -0
  60. package/dist/adapters/react/searchDevtoolsRenderersForClosestTarget.js +11 -0
  61. package/dist/adapters/svelte/svelteAdapter.d.ts +22 -0
  62. package/dist/adapters/svelte/svelteAdapter.js +88 -0
  63. package/dist/adapters/vue/getVNodeBoundingBox.d.ts +4 -0
  64. package/dist/adapters/vue/getVNodeBoundingBox.js +31 -0
  65. package/dist/adapters/vue/vueAdapter.d.ts +15 -0
  66. package/dist/adapters/vue/vueAdapter.js +113 -0
  67. package/dist/browserApi.d.ts +103 -0
  68. package/dist/browserApi.js +160 -0
  69. package/dist/components/Button.d.ts +4 -0
  70. package/dist/components/Button.js +17 -0
  71. package/dist/components/ComponentOutline.d.ts +7 -0
  72. package/dist/components/ComponentOutline.js +93 -0
  73. package/dist/components/MaybeOutline.d.ts +7 -0
  74. package/dist/components/MaybeOutline.js +43 -0
  75. package/dist/components/Outline.d.ts +25 -0
  76. package/dist/components/Outline.js +135 -0
  77. package/dist/components/RenderBoxes.d.ts +4 -0
  78. package/dist/components/RenderBoxes.js +73 -0
  79. package/dist/components/Runtime.d.ts +3 -0
  80. package/dist/components/Runtime.js +188 -0
  81. package/dist/components/SimpleNodeOutline.d.ts +4 -0
  82. package/dist/components/SimpleNodeOutline.js +47 -0
  83. package/dist/components/Toast.d.ts +4 -0
  84. package/dist/components/Toast.js +68 -0
  85. package/dist/components/Tooltip.d.ts +5 -0
  86. package/dist/components/Tooltip.js +21 -0
  87. package/dist/consts.d.ts +6 -0
  88. package/dist/consts.js +5 -0
  89. package/dist/functions/cropPath.d.ts +1 -0
  90. package/dist/functions/cropPath.js +9 -0
  91. package/dist/functions/cropPath.test.d.ts +1 -0
  92. package/dist/functions/cropPath.test.js +16 -0
  93. package/dist/functions/deduplicateLabels.d.ts +2 -0
  94. package/dist/functions/deduplicateLabels.js +12 -0
  95. package/dist/functions/evalTemplate.d.ts +3 -0
  96. package/dist/functions/evalTemplate.js +7 -0
  97. package/dist/functions/evalTemplate.test.d.ts +1 -0
  98. package/dist/functions/evalTemplate.test.js +11 -0
  99. package/dist/functions/findNames.d.ts +5 -0
  100. package/dist/functions/findNames.js +15 -0
  101. package/dist/functions/formatAncestryChain.d.ts +9 -0
  102. package/dist/functions/formatAncestryChain.js +56 -0
  103. package/dist/functions/getBoundingRect.d.ts +1 -0
  104. package/dist/functions/getBoundingRect.js +11 -0
  105. package/dist/functions/getComposedBoundingBox.d.ts +2 -0
  106. package/dist/functions/getComposedBoundingBox.js +20 -0
  107. package/dist/functions/getIdsOnPathToRoot.d.ts +3 -0
  108. package/dist/functions/getIdsOnPathToRoot.js +15 -0
  109. package/dist/functions/getMultipleElementsBoundingBox.d.ts +2 -0
  110. package/dist/functions/getMultipleElementsBoundingBox.js +20 -0
  111. package/dist/functions/getPathToParent.d.ts +1 -0
  112. package/dist/functions/getPathToParent.js +15 -0
  113. package/dist/functions/getReferenceId.d.ts +1 -0
  114. package/dist/functions/getReferenceId.js +9 -0
  115. package/dist/functions/getUsableFileName.d.ts +1 -0
  116. package/dist/functions/getUsableFileName.js +17 -0
  117. package/dist/functions/getUsableFileName.test.d.ts +1 -0
  118. package/dist/functions/getUsableFileName.test.js +16 -0
  119. package/dist/functions/getUsableName.d.ts +2 -0
  120. package/dist/functions/getUsableName.js +47 -0
  121. package/dist/functions/isCombinationModifiersPressed.d.ts +4 -0
  122. package/dist/functions/isCombinationModifiersPressed.js +16 -0
  123. package/dist/functions/isLocatorsOwnElement.d.ts +1 -0
  124. package/dist/functions/isLocatorsOwnElement.js +3 -0
  125. package/dist/functions/mergeRects.d.ts +2 -0
  126. package/dist/functions/mergeRects.js +10 -0
  127. package/dist/functions/mergeRects.test.d.ts +1 -0
  128. package/dist/functions/mergeRects.test.js +23 -0
  129. package/dist/functions/nonNullable.d.ts +1 -0
  130. package/dist/functions/nonNullable.js +3 -0
  131. package/dist/functions/parseDataId.d.ts +3 -0
  132. package/dist/functions/parseDataId.js +44 -0
  133. package/dist/functions/transformPath.d.ts +1 -0
  134. package/dist/functions/transformPath.js +7 -0
  135. package/dist/functions/transformPath.test.d.ts +1 -0
  136. package/dist/functions/transformPath.test.js +16 -0
  137. package/dist/global.d.js +1 -0
  138. package/dist/index.d.ts +11 -0
  139. package/dist/index.js +13 -0
  140. package/dist/initRuntime.d.ts +8 -0
  141. package/dist/initRuntime.js +80 -0
  142. package/dist/output.css +1733 -0
  143. package/dist/types/LabelData.d.ts +5 -0
  144. package/dist/types/LabelData.js +1 -0
  145. package/dist/types/TreeNode.d.ts +19 -0
  146. package/dist/types/TreeNode.js +1 -0
  147. package/dist/types/types.d.ts +53 -0
  148. package/dist/types/types.js +1 -0
  149. package/jest.config.ts +195 -0
  150. package/package.json +75 -0
  151. package/scripts/wrapCSS.js +26 -0
  152. package/scripts/wrapImage.js +24 -0
  153. package/src/_generated_styles.ts +1734 -0
  154. package/src/_generated_tree_icon.ts +2 -0
  155. package/src/adapters/HtmlElementTreeNode.ts +51 -0
  156. package/src/adapters/adapterApi.ts +35 -0
  157. package/src/adapters/createTreeNode.ts +25 -0
  158. package/src/adapters/getElementInfo.tsx +27 -0
  159. package/src/adapters/getParentsPath.tsx +49 -0
  160. package/src/adapters/getTree.tsx +45 -0
  161. package/src/adapters/goUpByTheTree.ts +20 -0
  162. package/src/adapters/jsx/getExpressionData.ts +47 -0
  163. package/src/adapters/jsx/getJSXComponentBoundingBox.ts +63 -0
  164. package/src/adapters/jsx/jsxAdapter.ts +276 -0
  165. package/src/adapters/jsx/runtimeStore.ts +94 -0
  166. package/src/adapters/react/fiberToSimple.tsx +72 -0
  167. package/src/adapters/react/findDebugSource.ts +15 -0
  168. package/src/adapters/react/findFiberByHtmlElement.ts +27 -0
  169. package/src/adapters/react/gatherFiberRoots.tsx +36 -0
  170. package/src/adapters/react/getAllFiberChildren.tsx +11 -0
  171. package/src/adapters/react/getAllParentsElementsAndRootComponent.ts +52 -0
  172. package/src/adapters/react/getAllWrappingParents.ts +25 -0
  173. package/src/adapters/react/getFiberComponentBoundingBox.ts +30 -0
  174. package/src/adapters/react/getFiberLabel.ts +20 -0
  175. package/src/adapters/react/getFiberOwnBoundingBox.ts +9 -0
  176. package/src/adapters/react/isStyled.ts +5 -0
  177. package/src/adapters/react/makeFiberId.tsx +19 -0
  178. package/src/adapters/react/reactAdapter.ts +148 -0
  179. package/src/adapters/react/searchDevtoolsRenderersForClosestTarget.ts +15 -0
  180. package/src/adapters/svelte/svelteAdapter.ts +111 -0
  181. package/src/adapters/vue/getVNodeBoundingBox.tsx +42 -0
  182. package/src/adapters/vue/vueAdapter.ts +139 -0
  183. package/src/assets/tree-icon.png +0 -0
  184. package/src/browserApi.ts +288 -0
  185. package/src/components/Button.tsx +14 -0
  186. package/src/components/ComponentOutline.tsx +98 -0
  187. package/src/components/MaybeOutline.tsx +49 -0
  188. package/src/components/Outline.tsx +153 -0
  189. package/src/components/RenderBoxes.tsx +57 -0
  190. package/src/components/Runtime.tsx +246 -0
  191. package/src/components/SimpleNodeOutline.tsx +27 -0
  192. package/src/components/Toast.tsx +83 -0
  193. package/src/components/Tooltip.tsx +28 -0
  194. package/src/consts.ts +7 -0
  195. package/src/functions/cropPath.test.ts +18 -0
  196. package/src/functions/cropPath.ts +12 -0
  197. package/src/functions/deduplicateLabels.ts +16 -0
  198. package/src/functions/evalTemplate.test.ts +12 -0
  199. package/src/functions/evalTemplate.ts +8 -0
  200. package/src/functions/findNames.ts +20 -0
  201. package/src/functions/formatAncestryChain.ts +80 -0
  202. package/src/functions/getBoundingRect.tsx +11 -0
  203. package/src/functions/getComposedBoundingBox.tsx +25 -0
  204. package/src/functions/getIdsOnPathToRoot.tsx +21 -0
  205. package/src/functions/getMultipleElementsBoundingBox.tsx +25 -0
  206. package/src/functions/getPathToParent.tsx +17 -0
  207. package/src/functions/getReferenceId.tsx +10 -0
  208. package/src/functions/getUsableFileName.test.tsx +24 -0
  209. package/src/functions/getUsableFileName.tsx +19 -0
  210. package/src/functions/getUsableName.ts +52 -0
  211. package/src/functions/isCombinationModifiersPressed.ts +32 -0
  212. package/src/functions/isLocatorsOwnElement.tsx +9 -0
  213. package/src/functions/mergeRects.test.ts +15 -0
  214. package/src/functions/mergeRects.tsx +12 -0
  215. package/src/functions/nonNullable.ts +3 -0
  216. package/src/functions/parseDataId.ts +62 -0
  217. package/src/functions/transformPath.test.ts +28 -0
  218. package/src/functions/transformPath.ts +7 -0
  219. package/src/global.d.ts +31 -0
  220. package/src/index.ts +18 -0
  221. package/src/initRuntime.ts +83 -0
  222. package/src/main.css +3 -0
  223. package/src/types/LabelData.ts +6 -0
  224. package/src/types/TreeNode.ts +22 -0
  225. package/src/types/types.ts +55 -0
  226. package/tailwind.config.js +9 -0
  227. package/tsconfig.json +20 -0
@@ -0,0 +1,276 @@
1
+ import type { FileStorage } from "@locator/shared";
2
+ import {
3
+ parseDataId,
4
+ parseDataPath,
5
+ splitFullPath,
6
+ } from "../../functions/parseDataId";
7
+ import type { TreeNode, TreeNodeComponent } from "../../types/TreeNode";
8
+ import type { Source } from "../../types/types";
9
+ import type {
10
+ AdapterObject,
11
+ FullElementInfo,
12
+ ParentPathItem,
13
+ TreeState,
14
+ } from "../adapterApi";
15
+ import { goUpByTheTree } from "../goUpByTheTree";
16
+ import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
17
+ import { getExpressionData } from "./getExpressionData";
18
+ import { getJSXComponentBoundingBox } from "./getJSXComponentBoundingBox";
19
+
20
+ export function getElementInfo(target: HTMLElement): FullElementInfo | null {
21
+ const found = target.closest("[data-locatorjs-id], [data-locatorjs]");
22
+
23
+ if (
24
+ found &&
25
+ found instanceof HTMLElement &&
26
+ found.dataset &&
27
+ (found.dataset.locatorjsId ||
28
+ found.dataset.locatorjs ||
29
+ found.dataset.locatorjsStyled)
30
+ ) {
31
+ const dataId = found.dataset.locatorjsId;
32
+ const dataPath = found.dataset.locatorjs;
33
+ const styledDataId = found.dataset.locatorjsStyled;
34
+
35
+ if (!dataId && !dataPath) {
36
+ return null;
37
+ }
38
+
39
+ let fileFullPath: string;
40
+
41
+ if (dataPath) {
42
+ const parsed = parseDataPath(dataPath);
43
+ if (!parsed) {
44
+ return null;
45
+ }
46
+ [fileFullPath] = parsed;
47
+ } else if (dataId) {
48
+ [fileFullPath] = parseDataId(dataId);
49
+ } else {
50
+ return null;
51
+ }
52
+
53
+ const locatorData = window.__LOCATOR_DATA__;
54
+ const fileData: FileStorage | undefined = locatorData?.[fileFullPath];
55
+
56
+ // Handle styled components (only when locatorData is available)
57
+ const [styledFileFullPath, styledId] = styledDataId
58
+ ? parseDataId(styledDataId)
59
+ : [null, null];
60
+ const styledFileData: FileStorage | undefined =
61
+ styledFileFullPath && locatorData?.[styledFileFullPath];
62
+ const styledExpData =
63
+ styledFileData && styledFileData.styledDefinitions[Number(styledId)];
64
+
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ const styledLink = styledExpData && {
67
+ filePath: styledFileData.filePath,
68
+ projectPath: styledFileData.projectPath,
69
+ column: (styledExpData.loc?.start.column || 0) + 1,
70
+ line: styledExpData.loc?.start.line || 0,
71
+ };
72
+
73
+ // Get expression data (works with or without locatorData)
74
+ const expData = getExpressionData(found, fileData || null);
75
+ if (!expData) {
76
+ return null;
77
+ }
78
+
79
+ // Extract file path components
80
+ let filePath: string;
81
+ let projectPath: string;
82
+
83
+ if (fileData) {
84
+ filePath = fileData.filePath;
85
+ projectPath = fileData.projectPath;
86
+ } else {
87
+ // If no fileData, split the full path
88
+ [projectPath, filePath] = splitFullPath(fileFullPath);
89
+ }
90
+
91
+ const wrappingComponent =
92
+ expData.wrappingComponentId !== null && fileData
93
+ ? fileData.components[Number(expData.wrappingComponentId)]
94
+ : null;
95
+
96
+ return {
97
+ thisElement: {
98
+ box: found.getBoundingClientRect(),
99
+ label: expData.name,
100
+ link: {
101
+ filePath,
102
+ projectPath,
103
+ column: (expData.loc.start.column || 0) + 1,
104
+ line: expData.loc.start.line || 0,
105
+ },
106
+ },
107
+ htmlElement: found,
108
+ parentElements: [],
109
+ componentBox: getJSXComponentBoundingBox(
110
+ found,
111
+ locatorData || {},
112
+ fileFullPath,
113
+ Number(expData.wrappingComponentId)
114
+ ),
115
+ componentsLabels: wrappingComponent
116
+ ? [
117
+ {
118
+ label: wrappingComponent.name || "component",
119
+ link: {
120
+ filePath,
121
+ projectPath,
122
+ column: (wrappingComponent.loc?.start.column || 0) + 1,
123
+ line: wrappingComponent.loc?.start.line || 0,
124
+ },
125
+ },
126
+ ]
127
+ : [],
128
+ };
129
+ }
130
+
131
+ // return deduplicateLabels(labels);
132
+
133
+ return null;
134
+ }
135
+
136
+ export class JSXTreeNodeElement extends HtmlElementTreeNode {
137
+ getSource(): Source | null {
138
+ const dataId = this.element.dataset.locatorjsId;
139
+ const dataPath = this.element.dataset.locatorjs;
140
+
141
+ if (!dataId && !dataPath) {
142
+ return null;
143
+ }
144
+
145
+ let fileFullPath: string;
146
+
147
+ if (dataPath) {
148
+ const parsed = parseDataPath(dataPath);
149
+ if (!parsed) {
150
+ return null;
151
+ }
152
+ [fileFullPath] = parsed;
153
+ } else if (dataId) {
154
+ [fileFullPath] = parseDataId(dataId);
155
+ } else {
156
+ return null;
157
+ }
158
+
159
+ const locatorData = window.__LOCATOR_DATA__;
160
+ const fileData: FileStorage | undefined = locatorData?.[fileFullPath];
161
+
162
+ // Get expression data (works with or without locatorData)
163
+ const expData = getExpressionData(this.element, fileData || null);
164
+ if (expData) {
165
+ let fileName: string;
166
+ let projectPath: string;
167
+
168
+ if (fileData) {
169
+ fileName = fileData.filePath;
170
+ projectPath = fileData.projectPath;
171
+ } else {
172
+ // If no fileData, split the full path
173
+ [projectPath, fileName] = splitFullPath(fileFullPath);
174
+ }
175
+
176
+ return {
177
+ fileName,
178
+ projectPath,
179
+ columnNumber: (expData.loc.start.column || 0) + 1,
180
+ lineNumber: expData.loc.start.line || 0,
181
+ };
182
+ }
183
+
184
+ return null;
185
+ }
186
+ getComponent(): TreeNodeComponent | null {
187
+ const dataId = this.element.dataset.locatorjsId;
188
+ const dataPath = this.element.dataset.locatorjs;
189
+
190
+ if (!dataId && !dataPath) {
191
+ return null;
192
+ }
193
+
194
+ let fileFullPath: string;
195
+
196
+ if (dataPath) {
197
+ const parsed = parseDataPath(dataPath);
198
+ if (!parsed) {
199
+ return null;
200
+ }
201
+ [fileFullPath] = parsed;
202
+ } else if (dataId) {
203
+ [fileFullPath] = parseDataId(dataId);
204
+ } else {
205
+ return null;
206
+ }
207
+
208
+ const locatorData = window.__LOCATOR_DATA__;
209
+ const fileData: FileStorage | undefined = locatorData?.[fileFullPath];
210
+
211
+ // Component information is only available when we have fileData
212
+ if (fileData) {
213
+ const expData = getExpressionData(this.element, fileData);
214
+ if (expData && expData.wrappingComponentId !== null) {
215
+ const component = fileData.components[expData.wrappingComponentId];
216
+ if (component) {
217
+ return {
218
+ label: component.name || "component",
219
+ definitionLink: {
220
+ fileName: fileData.filePath,
221
+ projectPath: fileData.projectPath,
222
+ columnNumber: (component.loc?.start.column || 0) + 1,
223
+ lineNumber: component.loc?.start.line || 0,
224
+ },
225
+ };
226
+ }
227
+ }
228
+ }
229
+
230
+ return null;
231
+ }
232
+ }
233
+
234
+ function getTree(element: HTMLElement): TreeState | null {
235
+ const originalRoot: TreeNode = new JSXTreeNodeElement(element);
236
+
237
+ return goUpByTheTree(originalRoot);
238
+ }
239
+
240
+ function getParentsPaths(element: HTMLElement): ParentPathItem[] {
241
+ const path: ParentPathItem[] = [];
242
+ let currentElement: HTMLElement | null = element;
243
+ let previousComponentKey: string | null = null;
244
+
245
+ do {
246
+ if (currentElement) {
247
+ const info = getElementInfo(currentElement);
248
+ const currentComponentKey = JSON.stringify(info?.componentsLabels);
249
+ if (info && currentComponentKey !== previousComponentKey) {
250
+ previousComponentKey = currentComponentKey;
251
+
252
+ const link = info.thisElement.link;
253
+ const label = info.thisElement.label;
254
+
255
+ if (link) {
256
+ path.push({
257
+ title: label,
258
+ link: link,
259
+ });
260
+ }
261
+ }
262
+ }
263
+
264
+ currentElement = currentElement.parentElement;
265
+ } while (currentElement);
266
+
267
+ return path;
268
+ }
269
+
270
+ const jsxAdapter: AdapterObject = {
271
+ getElementInfo,
272
+ getTree,
273
+ getParentsPaths,
274
+ };
275
+
276
+ export default jsxAdapter;
@@ -0,0 +1,94 @@
1
+ import {
2
+ parseDataId,
3
+ parseDataPath,
4
+ splitFullPath,
5
+ } from "../../functions/parseDataId";
6
+
7
+ export function getDataForDataId(dataId: string) {
8
+ let fileFullPath: string;
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ let expData: any;
11
+ let filePath = "";
12
+ let projectPath = "";
13
+
14
+ const data = window.__LOCATOR_DATA__;
15
+
16
+ // Try parsing as path format first (new format)
17
+ const pathParsed = parseDataPath(dataId);
18
+ if (pathParsed) {
19
+ const [fullPath, line, column] = pathParsed;
20
+ fileFullPath = fullPath;
21
+
22
+ if (data) {
23
+ const fileData = data[fileFullPath];
24
+ if (fileData) {
25
+ // Find expression by location
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ expData = fileData.expressions.find(
28
+ (exp: any) =>
29
+ exp.loc.start.line === line && exp.loc.start.column === column
30
+ );
31
+ if (expData) {
32
+ filePath = fileData.filePath;
33
+ projectPath = fileData.projectPath;
34
+ }
35
+ }
36
+ }
37
+
38
+ // If no data or not found, create minimal data from path
39
+ if (!expData) {
40
+ [projectPath, filePath] = splitFullPath(fullPath);
41
+ expData = {
42
+ name: "Component",
43
+ loc: {
44
+ start: { line, column },
45
+ end: { line, column },
46
+ },
47
+ wrappingComponentId: null,
48
+ };
49
+ }
50
+ } else {
51
+ // Fall back to ID format (old format) - requires __LOCATOR_DATA__
52
+ if (!data) {
53
+ return null;
54
+ }
55
+
56
+ const [fullPath, id] = parseDataId(dataId);
57
+ fileFullPath = fullPath;
58
+ const fileData = data[fileFullPath];
59
+ if (!fileData) {
60
+ return null;
61
+ }
62
+ expData = fileData.expressions[Number(id)];
63
+ filePath = fileData.filePath;
64
+ projectPath = fileData.projectPath;
65
+ }
66
+
67
+ if (!expData) {
68
+ return null;
69
+ }
70
+
71
+ const link = {
72
+ filePath,
73
+ projectPath,
74
+ column: expData.loc.start.column || 0,
75
+ line: expData.loc.start.line || 0,
76
+ };
77
+
78
+ // let label;
79
+ // if (expData.type === "jsx") {
80
+ // label =
81
+ // (expData.wrappingComponent ? `${expData.wrappingComponent}: ` : "") +
82
+ // expData.name;
83
+ // } else {
84
+ // label = `${expData.htmlTag ? `styled.${expData.htmlTag}` : "styled"}${
85
+ // expData.name ? `: ${expData.name}` : ""
86
+ // }`;
87
+ // }
88
+
89
+ return {
90
+ link,
91
+ label: expData.name,
92
+ componentLabel: expData,
93
+ };
94
+ }
@@ -0,0 +1,72 @@
1
+ import { Fiber } from "@locator/shared";
2
+ import { getBoundingRect } from "../../functions/getBoundingRect";
3
+ import { getComposedBoundingBox } from "../../functions/getComposedBoundingBox";
4
+ import { getUsableName } from "../../functions/getUsableName";
5
+
6
+ import { getAllFiberChildren } from "./getAllFiberChildren";
7
+ import { SimpleNode } from "../../types/types";
8
+ import { makeFiberId } from "./makeFiberId";
9
+
10
+ export function fiberToSimple(
11
+ fiber: Fiber,
12
+ manualChildren?: SimpleNode[]
13
+ ): SimpleNode {
14
+ let simpleChildren;
15
+ if (fiber.elementType?.styledComponentId) {
16
+ const children = getAllFiberChildren(fiber);
17
+ if (children.length === 1) {
18
+ const simple = fiberToSimple(children[0]!, manualChildren);
19
+ simple.name = `${simple.name} (styled)`;
20
+ return simple;
21
+ }
22
+ }
23
+
24
+ if (manualChildren) {
25
+ simpleChildren = manualChildren;
26
+ } else {
27
+ const children = getAllFiberChildren(fiber);
28
+
29
+ simpleChildren = children.map((child) => {
30
+ return fiberToSimple(child);
31
+ });
32
+ }
33
+
34
+ const element =
35
+ fiber.stateNode instanceof Element || fiber.stateNode instanceof Text
36
+ ? fiber.stateNode
37
+ : fiber.stateNode?.containerInfo;
38
+
39
+ if (element) {
40
+ const box = getBoundingRect(element);
41
+ return {
42
+ type: "element",
43
+ element: element,
44
+ fiber: fiber,
45
+ uniqueId: makeFiberId(fiber),
46
+ name: getUsableName(fiber),
47
+ box: box || getComposedBoundingBox(simpleChildren),
48
+ children: simpleChildren,
49
+ source: fiber._debugSource || null,
50
+ };
51
+ } else {
52
+ return {
53
+ type: "component",
54
+ fiber: fiber,
55
+ uniqueId: makeFiberId(fiber),
56
+ name: getUsableName(fiber),
57
+ box: getComposedBoundingBox(simpleChildren),
58
+ children: simpleChildren,
59
+ source: fiber._debugSource || null,
60
+ definitionSourceFile: simpleChildren.reduce<string | null>(
61
+ (acc, curr) => {
62
+ if (curr.source?.fileName) {
63
+ return curr.source?.fileName;
64
+ } else {
65
+ return acc;
66
+ }
67
+ },
68
+ null
69
+ ),
70
+ };
71
+ }
72
+ }
@@ -0,0 +1,15 @@
1
+ import { Fiber, Source } from "@locator/shared";
2
+
3
+ export function findDebugSource(
4
+ fiber: Fiber
5
+ ): { fiber: Fiber; source: Source } | null {
6
+ let current: Fiber | null = fiber;
7
+ while (current) {
8
+ if (current._debugSource) {
9
+ return { fiber: current, source: current._debugSource };
10
+ }
11
+ current = current._debugOwner || null;
12
+ }
13
+
14
+ return null;
15
+ }
@@ -0,0 +1,27 @@
1
+ import { Fiber, Renderer } from "@locator/shared";
2
+ import { findDebugSource } from "./findDebugSource";
3
+
4
+ export function findFiberByHtmlElement(
5
+ target: HTMLElement,
6
+ shouldHaveDebugSource: boolean
7
+ ): Fiber | null {
8
+ const renderers = window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers;
9
+ // console.log("RENDERERS: ", renderers);
10
+ const renderersValues = renderers?.values();
11
+ if (renderersValues) {
12
+ for (const renderer of Array.from(renderersValues) as Renderer[]) {
13
+ if (renderer.findFiberByHostInstance) {
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ const found = renderer.findFiberByHostInstance(target as any);
16
+ if (found) {
17
+ if (shouldHaveDebugSource) {
18
+ return findDebugSource(found)?.fiber || null;
19
+ } else {
20
+ return found;
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ return null;
27
+ }
@@ -0,0 +1,36 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Fiber } from "@locator/shared";
3
+ import { findFiberByHtmlElement } from "./findFiberByHtmlElement";
4
+
5
+ export function gatherFiberRoots(
6
+ parentNode: HTMLElement,
7
+ mutable_foundFibers: Fiber[]
8
+ ) {
9
+ const nodes = parentNode.childNodes;
10
+ for (let i = 0; i < nodes.length; i++) {
11
+ const node = nodes[i];
12
+ if (node instanceof HTMLElement) {
13
+ const fiber =
14
+ (node as any)._reactRootContainer?._internalRoot?.current ||
15
+ (node as any)._reactRootContainer?.current;
16
+ if (fiber) {
17
+ mutable_foundFibers.push(getRoot(fiber));
18
+ } else {
19
+ const rootFiber = findFiberByHtmlElement(node!, false);
20
+ if (rootFiber) {
21
+ mutable_foundFibers.push(getRoot(rootFiber));
22
+ } else {
23
+ gatherFiberRoots(node, mutable_foundFibers);
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ function getRoot(fiber: Fiber): Fiber {
31
+ let thisFiber: Fiber = fiber;
32
+ while (thisFiber.return) {
33
+ thisFiber = thisFiber.return;
34
+ }
35
+ return thisFiber;
36
+ }
@@ -0,0 +1,11 @@
1
+ import { Fiber } from "@locator/shared";
2
+
3
+ export function getAllFiberChildren(fiber: Fiber) {
4
+ const allChildren: Fiber[] = [];
5
+ let child = fiber.child;
6
+ while (child) {
7
+ allChildren.push(child);
8
+ child = child.sibling;
9
+ }
10
+ return allChildren;
11
+ }
@@ -0,0 +1,52 @@
1
+ import { Fiber } from "@locator/shared";
2
+ import { getUsableName } from "../../functions/getUsableName";
3
+ import { mergeRects } from "../../functions/mergeRects";
4
+ import { SimpleDOMRect } from "../../types/types";
5
+ import { ElementInfo } from "../adapterApi";
6
+ import { getFiberComponentBoundingBox } from "./getFiberComponentBoundingBox";
7
+ import { isStyledElement } from "./isStyled";
8
+
9
+ export function getAllParentsElementsAndRootComponent(fiber: Fiber): {
10
+ component: Fiber;
11
+ componentBox: SimpleDOMRect;
12
+ parentElements: ElementInfo[];
13
+ } {
14
+ const parentElements: ElementInfo[] = [];
15
+ const deepestElement = fiber.stateNode;
16
+ if (!deepestElement || !(deepestElement instanceof HTMLElement)) {
17
+ throw new Error(
18
+ "This functions works only for Fibres with HTMLElement stateNode"
19
+ );
20
+ }
21
+ let componentBox: SimpleDOMRect = deepestElement.getBoundingClientRect();
22
+
23
+ // For styled-components we rather use parent element
24
+ let currentFiber =
25
+ isStyledElement(fiber) && fiber._debugOwner ? fiber._debugOwner : fiber;
26
+ while (currentFiber._debugOwner || currentFiber.return) {
27
+ currentFiber = currentFiber._debugOwner || currentFiber.return!;
28
+ const currentElement = currentFiber.stateNode;
29
+ if (!currentElement || !(currentElement instanceof HTMLElement)) {
30
+ return {
31
+ component: currentFiber,
32
+ parentElements,
33
+ componentBox:
34
+ getFiberComponentBoundingBox(currentFiber) || componentBox,
35
+ };
36
+ }
37
+
38
+ const usableName = getUsableName(currentFiber);
39
+
40
+ componentBox = mergeRects(
41
+ componentBox,
42
+ currentElement.getBoundingClientRect()
43
+ );
44
+
45
+ parentElements.push({
46
+ box: currentElement.getBoundingClientRect(),
47
+ label: usableName,
48
+ link: null,
49
+ });
50
+ }
51
+ throw new Error("Could not find root component");
52
+ }
@@ -0,0 +1,25 @@
1
+ import { Fiber } from "@locator/shared";
2
+ import { getAllFiberChildren } from "./getAllFiberChildren";
3
+
4
+ export function getAllWrappingParents(fiber: Fiber): Fiber[] {
5
+ const parents: Fiber[] = [fiber];
6
+
7
+ let currentFiber = fiber;
8
+ while (currentFiber.return) {
9
+ currentFiber = currentFiber.return;
10
+ if (
11
+ currentFiber.stateNode &&
12
+ currentFiber.stateNode instanceof HTMLElement
13
+ ) {
14
+ return parents;
15
+ }
16
+
17
+ // if there is multiple children, it means the parent is not just wrapping this one
18
+ const children = getAllFiberChildren(currentFiber);
19
+ if (children.length > 1) {
20
+ return parents;
21
+ }
22
+ parents.push(currentFiber);
23
+ }
24
+ return parents;
25
+ }
@@ -0,0 +1,30 @@
1
+ import { getFiberOwnBoundingBox } from "./getFiberOwnBoundingBox";
2
+ import { Fiber } from "@locator/shared";
3
+ import { getAllFiberChildren } from "./getAllFiberChildren";
4
+ import { mergeRects } from "../../functions/mergeRects";
5
+ import { SimpleDOMRect } from "../../types/types";
6
+
7
+ const MAX_LEVEL = 6;
8
+ export function getFiberComponentBoundingBox(fiber: Fiber, level = 0) {
9
+ const children = getAllFiberChildren(fiber);
10
+ let composedRect: SimpleDOMRect | undefined;
11
+ children.forEach((child) => {
12
+ let box = getFiberOwnBoundingBox(child);
13
+ if (!box && level < MAX_LEVEL) {
14
+ box = getFiberComponentBoundingBox(child, level + 1) || null;
15
+ }
16
+ if (!box) {
17
+ return;
18
+ }
19
+ if (box.width <= 0 || box.height <= 0) {
20
+ // ignore zero-sized rects
21
+ return;
22
+ }
23
+ if (composedRect) {
24
+ composedRect = mergeRects(composedRect, box);
25
+ } else {
26
+ composedRect = box;
27
+ }
28
+ });
29
+ return composedRect;
30
+ }
@@ -0,0 +1,20 @@
1
+ import { Fiber, Source } from "@locator/shared";
2
+ import { LabelData } from "../../types/LabelData";
3
+ import { getUsableName } from "../../functions/getUsableName";
4
+
5
+ export function getFiberLabel(fiber: Fiber, source?: Source): LabelData {
6
+ const name = getUsableName(fiber);
7
+
8
+ const label: LabelData = {
9
+ label: name,
10
+ link: source
11
+ ? {
12
+ filePath: source.fileName,
13
+ projectPath: "",
14
+ line: source.lineNumber,
15
+ column: source.columnNumber || 0,
16
+ }
17
+ : null,
18
+ };
19
+ return label;
20
+ }
@@ -0,0 +1,9 @@
1
+ import { Fiber } from "@locator/shared";
2
+ import { SimpleDOMRect } from "../../types/types";
3
+
4
+ export function getFiberOwnBoundingBox(fiber: Fiber): SimpleDOMRect | null {
5
+ if (fiber.stateNode && fiber.stateNode.getBoundingClientRect) {
6
+ return fiber.stateNode.getBoundingClientRect();
7
+ }
8
+ return null;
9
+ }
@@ -0,0 +1,5 @@
1
+ import { Fiber } from "@locator/shared";
2
+
3
+ export function isStyledElement(fiber: Fiber) {
4
+ return !!fiber._debugOwner?.elementType?.styledComponentId;
5
+ }