@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,114 @@
1
+ import { findDebugSource } from "./findDebugSource";
2
+ import { findFiberByHtmlElement } from "./findFiberByHtmlElement";
3
+ import { getFiberLabel } from "./getFiberLabel";
4
+ import { getAllWrappingParents } from "./getAllWrappingParents";
5
+ import { deduplicateLabels } from "../../functions/deduplicateLabels";
6
+ import { getFiberOwnBoundingBox } from "./getFiberOwnBoundingBox";
7
+ import { getAllParentsElementsAndRootComponent } from "./getAllParentsElementsAndRootComponent";
8
+ import { isStyledElement } from "./isStyled";
9
+ import { goUpByTheTree } from "../goUpByTheTree";
10
+ import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
11
+ export function getElementInfo(found) {
12
+ // Instead of labels, return this element, parent elements leading to closest component, its component labels, all wrapping components labels.
13
+ const labels = [];
14
+ const fiber = findFiberByHtmlElement(found, false);
15
+ if (fiber) {
16
+ const {
17
+ component,
18
+ componentBox,
19
+ parentElements
20
+ } = getAllParentsElementsAndRootComponent(fiber);
21
+ const allPotentialComponentFibers = getAllWrappingParents(component);
22
+
23
+ // This handles a common case when the component root is basically the comopnent itself, so I want to go to usage of the component
24
+ // TODO: whaat? why? currently I see that it adds the original styled components which is not necessary.
25
+
26
+ // if (fiber.return && fiber.return === fiber._debugOwner) {
27
+ // allPotentialComponentFibers.unshift(fiber.return);
28
+ // }
29
+
30
+ allPotentialComponentFibers.forEach(fiber => {
31
+ const fiberWithSource = findDebugSource(fiber);
32
+ if (fiberWithSource) {
33
+ const label = getFiberLabel(fiberWithSource.fiber, fiberWithSource.source);
34
+ labels.push(label);
35
+ }
36
+ });
37
+ const thisLabel = getFiberLabel(fiber, findDebugSource(fiber)?.source);
38
+ if (isStyledElement(fiber)) {
39
+ thisLabel.label = `${thisLabel.label} (styled)`;
40
+ }
41
+ return {
42
+ thisElement: {
43
+ box: getFiberOwnBoundingBox(fiber) || found.getBoundingClientRect(),
44
+ ...thisLabel
45
+ },
46
+ htmlElement: found,
47
+ parentElements: parentElements,
48
+ componentBox,
49
+ componentsLabels: deduplicateLabels(labels)
50
+ };
51
+ }
52
+ return null;
53
+ }
54
+ export class ReactTreeNodeElement extends HtmlElementTreeNode {
55
+ getSource() {
56
+ const fiber = findFiberByHtmlElement(this.element, false);
57
+ if (fiber && fiber._debugSource) {
58
+ return {
59
+ fileName: fiber._debugSource.fileName,
60
+ lineNumber: fiber._debugSource.lineNumber,
61
+ columnNumber: fiber._debugSource.columnNumber
62
+ };
63
+ }
64
+ return null;
65
+ }
66
+ getComponent() {
67
+ const fiber = findFiberByHtmlElement(this.element, false);
68
+ const componentFiber = fiber?._debugOwner;
69
+ if (componentFiber) {
70
+ const fiberLabel = getFiberLabel(componentFiber, findDebugSource(componentFiber)?.source);
71
+ return {
72
+ label: fiberLabel.label,
73
+ callLink: fiberLabel.link && {
74
+ fileName: fiberLabel.link.filePath,
75
+ lineNumber: fiberLabel.link.line,
76
+ columnNumber: fiberLabel.link.column,
77
+ projectPath: fiberLabel.link.projectPath
78
+ } || undefined
79
+ };
80
+ }
81
+ return null;
82
+ }
83
+ }
84
+ function getTree(element) {
85
+ const originalRoot = new ReactTreeNodeElement(element);
86
+ return goUpByTheTree(originalRoot);
87
+ }
88
+ function fiberToPathItem(fiber) {
89
+ const label = getFiberLabel(fiber, findDebugSource(fiber)?.source);
90
+ return {
91
+ title: label.label,
92
+ link: label.link
93
+ };
94
+ }
95
+ function getParentsPaths(element) {
96
+ const fiber = findFiberByHtmlElement(element, false);
97
+ if (fiber) {
98
+ const pathItems = [];
99
+ let currentFiber = fiber;
100
+ pathItems.push(fiberToPathItem(currentFiber));
101
+ while (currentFiber._debugOwner) {
102
+ currentFiber = currentFiber._debugOwner;
103
+ pathItems.push(fiberToPathItem(currentFiber));
104
+ }
105
+ return pathItems;
106
+ }
107
+ return [];
108
+ }
109
+ const reactAdapter = {
110
+ getElementInfo,
111
+ getTree,
112
+ getParentsPaths
113
+ };
114
+ export default reactAdapter;
@@ -0,0 +1 @@
1
+ export declare function searchDevtoolsRenderersForClosestTarget(target: HTMLElement): HTMLElement | null;
@@ -0,0 +1,11 @@
1
+ import { findFiberByHtmlElement } from "./findFiberByHtmlElement";
2
+ export function searchDevtoolsRenderersForClosestTarget(target) {
3
+ let closest = target;
4
+ while (closest) {
5
+ if (findFiberByHtmlElement(closest, false)) {
6
+ return closest;
7
+ }
8
+ closest = closest.parentElement;
9
+ }
10
+ return null;
11
+ }
@@ -0,0 +1,22 @@
1
+ import { Source } from "@locator/shared";
2
+ import { TreeNodeComponent } from "../../types/TreeNode";
3
+ import { AdapterObject, FullElementInfo } from "../adapterApi";
4
+ import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
5
+ type SvelteLoc = {
6
+ char: number;
7
+ column: number;
8
+ file: string;
9
+ line: number;
10
+ };
11
+ type SvelteElement = HTMLElement & {
12
+ __svelte_meta?: {
13
+ loc: SvelteLoc;
14
+ };
15
+ };
16
+ export declare function getElementInfo(found: SvelteElement): FullElementInfo | null;
17
+ export declare class SvelteTreeNodeElement extends HtmlElementTreeNode {
18
+ getSource(): Source | null;
19
+ getComponent(): TreeNodeComponent | null;
20
+ }
21
+ declare const svelteAdapter: AdapterObject;
22
+ export default svelteAdapter;
@@ -0,0 +1,88 @@
1
+ import { goUpByTheTree } from "../goUpByTheTree";
2
+ import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
3
+ export function getElementInfo(found) {
4
+ if (found.__svelte_meta) {
5
+ const {
6
+ loc
7
+ } = found.__svelte_meta;
8
+ return {
9
+ thisElement: {
10
+ box: found.getBoundingClientRect(),
11
+ label: found.nodeName.toLowerCase(),
12
+ link: {
13
+ column: loc.column + 1,
14
+ line: loc.line + 1,
15
+ filePath: loc.file,
16
+ projectPath: ""
17
+ }
18
+ },
19
+ htmlElement: found,
20
+ parentElements: [],
21
+ componentBox: found.getBoundingClientRect(),
22
+ componentsLabels: []
23
+ };
24
+ }
25
+ return null;
26
+ }
27
+ export class SvelteTreeNodeElement extends HtmlElementTreeNode {
28
+ getSource() {
29
+ const element = this.element;
30
+ if (element.__svelte_meta) {
31
+ const {
32
+ loc
33
+ } = element.__svelte_meta;
34
+ return {
35
+ fileName: loc.file,
36
+ lineNumber: loc.line + 1,
37
+ columnNumber: loc.column + 1
38
+ };
39
+ }
40
+ return null;
41
+ }
42
+ getComponent() {
43
+ return null;
44
+ }
45
+ }
46
+ function getTree(element) {
47
+ const originalRoot = new SvelteTreeNodeElement(element);
48
+ return goUpByTheTree(originalRoot);
49
+ }
50
+ function getParentsPaths(element) {
51
+ const path = [];
52
+ let currentElement = element;
53
+ let maxDepth = 10000; // just in case, it should not be needed
54
+
55
+ do {
56
+ if (currentElement?.__svelte_meta) {
57
+ const {
58
+ loc
59
+ } = currentElement.__svelte_meta;
60
+
61
+ // we assume that there is one component per file
62
+ const isSameFileAsTheLastOne = loc.file === path[path.length - 1]?.link?.filePath;
63
+ if (!isSameFileAsTheLastOne) {
64
+ path.push({
65
+ title: currentElement.nodeName.toLowerCase(),
66
+ link: {
67
+ column: loc.column + 1,
68
+ line: loc.line + 1,
69
+ filePath: loc.file,
70
+ projectPath: ""
71
+ }
72
+ });
73
+ }
74
+ }
75
+ currentElement = currentElement.parentElement;
76
+ maxDepth--;
77
+ if (maxDepth < 0) {
78
+ break;
79
+ }
80
+ } while (currentElement);
81
+ return path;
82
+ }
83
+ const svelteAdapter = {
84
+ getElementInfo,
85
+ getTree,
86
+ getParentsPaths
87
+ };
88
+ export default svelteAdapter;
@@ -0,0 +1,4 @@
1
+ import { SimpleDOMRect } from "../../types/types";
2
+ import type { ComponentInternalInstance, VNode } from "vue";
3
+ export declare function getVueComponentBoundingBox(vcomponent: ComponentInternalInstance): null;
4
+ export declare function getVNodeBoundingBox(vnode: VNode): SimpleDOMRect | null;
@@ -0,0 +1,31 @@
1
+ import { mergeRects } from "../../functions/mergeRects";
2
+ export function getVueComponentBoundingBox(vcomponent) {
3
+ let composedRect = null;
4
+ if (vcomponent?.subTree?.children && vcomponent?.subTree?.children instanceof Array) {
5
+ vcomponent?.subTree?.children.forEach(child => {
6
+ const box = getVNodeBoundingBox(child);
7
+ if (!box) {
8
+ return;
9
+ }
10
+ if (box.width <= 0 || box.height <= 0) {
11
+ // ignore zero-sized rects
12
+ return;
13
+ }
14
+ if (composedRect) {
15
+ composedRect = mergeRects(composedRect, box);
16
+ } else {
17
+ composedRect = box;
18
+ }
19
+ });
20
+ }
21
+ return composedRect;
22
+ }
23
+ export function getVNodeBoundingBox(vnode) {
24
+ if (vnode.el instanceof HTMLElement) {
25
+ return vnode.el.getBoundingClientRect();
26
+ }
27
+ if (vnode.component) {
28
+ return getVueComponentBoundingBox(vnode.component);
29
+ }
30
+ return null;
31
+ }
@@ -0,0 +1,15 @@
1
+ import { Source } from "@locator/shared";
2
+ import type { ComponentInternalInstance } from "vue";
3
+ import { TreeNodeComponent } from "../../types/TreeNode";
4
+ import { AdapterObject, FullElementInfo } from "../adapterApi";
5
+ import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
6
+ type VueElement = HTMLElement & {
7
+ __vueParentComponent?: ComponentInternalInstance;
8
+ };
9
+ export declare function getElementInfo(found: VueElement): FullElementInfo | null;
10
+ export declare class VueTreeNodeElement extends HtmlElementTreeNode {
11
+ getSource(): Source | null;
12
+ getComponent(): TreeNodeComponent | null;
13
+ }
14
+ declare const vueAdapter: AdapterObject;
15
+ export default vueAdapter;
@@ -0,0 +1,113 @@
1
+ import { goUpByTheTree } from "../goUpByTheTree";
2
+ import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
3
+ import { getVueComponentBoundingBox } from "./getVNodeBoundingBox";
4
+ export function getElementInfo(found) {
5
+ const parentComponent = found.__vueParentComponent;
6
+ if (parentComponent) {
7
+ if (!parentComponent.type) {
8
+ return null;
9
+ }
10
+ const componentBBox = getVueComponentBoundingBox(parentComponent);
11
+ const {
12
+ __file,
13
+ __name
14
+ } = parentComponent.type;
15
+ if (__file && __name) {
16
+ return {
17
+ thisElement: {
18
+ box: found.getBoundingClientRect(),
19
+ label: found.nodeName.toLowerCase(),
20
+ link: {
21
+ column: 1,
22
+ line: 1,
23
+ filePath: __file,
24
+ projectPath: ""
25
+ }
26
+ },
27
+ htmlElement: found,
28
+ parentElements: [],
29
+ componentBox: componentBBox || found.getBoundingClientRect(),
30
+ componentsLabels: [{
31
+ label: __name,
32
+ link: {
33
+ column: 1,
34
+ line: 1,
35
+ filePath: __file,
36
+ projectPath: ""
37
+ }
38
+ }]
39
+ };
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+ export class VueTreeNodeElement extends HtmlElementTreeNode {
45
+ getSource() {
46
+ const element = this.element;
47
+ const parentComponent = element.__vueParentComponent;
48
+ if (parentComponent && parentComponent.type) {
49
+ const {
50
+ __file
51
+ } = parentComponent.type;
52
+ if (__file) {
53
+ return {
54
+ fileName: __file,
55
+ lineNumber: 1,
56
+ columnNumber: 1
57
+ };
58
+ }
59
+ }
60
+ return null;
61
+ }
62
+ getComponent() {
63
+ return null;
64
+ }
65
+ // getComponent(): TreeNodeComponent | null {
66
+ // const element = this.element as VueElement;
67
+ // const parentComponent = element.__vueParentComponent;
68
+ // if (parentComponent && parentComponent.type) {
69
+ // const { __name } = parentComponent.type;
70
+ // if (__name) {
71
+ // return {
72
+ // label: __name,
73
+ // definitionLink: this.getSource() || undefined,
74
+ // };
75
+ // }
76
+ // }
77
+ // return null;
78
+ // }
79
+ }
80
+ function getTree(element) {
81
+ const originalRoot = new VueTreeNodeElement(element);
82
+ return goUpByTheTree(originalRoot);
83
+ }
84
+ function getParentsPaths(element) {
85
+ const path = [];
86
+ let currentElement = element;
87
+ let previousComponentKey = null;
88
+ do {
89
+ if (currentElement) {
90
+ const info = getElementInfo(currentElement);
91
+ const currentComponentKey = JSON.stringify(info?.componentsLabels);
92
+ if (info && currentComponentKey !== previousComponentKey) {
93
+ previousComponentKey = currentComponentKey;
94
+ const link = info.thisElement.link;
95
+ const label = info.thisElement.label;
96
+ if (link) {
97
+ path.push({
98
+ title: label,
99
+ link: link
100
+ });
101
+ }
102
+ }
103
+ }
104
+ currentElement = currentElement.parentElement;
105
+ } while (currentElement);
106
+ return path;
107
+ }
108
+ const vueAdapter = {
109
+ getElementInfo,
110
+ getTree,
111
+ getParentsPaths
112
+ };
113
+ export default vueAdapter;
@@ -0,0 +1,103 @@
1
+ import { AdapterId } from "./consts";
2
+ import { AncestryItem } from "./functions/formatAncestryChain";
3
+ export interface LocatorJSAPI {
4
+ /**
5
+ * Get formatted ancestry path for an element.
6
+ * Returns a human-readable string showing the component hierarchy from root to target element.
7
+ *
8
+ * @param elementOrSelector - HTMLElement or CSS selector string (e.g., 'button.submit', '#login-form')
9
+ * @returns Formatted ancestry chain as string, or null if element not found/unsupported
10
+ *
11
+ * @example
12
+ * // Basic usage with CSS selector
13
+ * window.__locatorjs__.getPath('button.submit');
14
+ * // Returns:
15
+ * // "div in App at src/App.tsx:15
16
+ * // └─ button in SubmitButton at src/components/SubmitButton.tsx:12"
17
+ *
18
+ * @example
19
+ * // Usage with HTMLElement
20
+ * const button = document.querySelector('button.submit');
21
+ * window.__locatorjs__.getPath(button);
22
+ *
23
+ * @example
24
+ * // In Playwright
25
+ * const path = await page.evaluate(() => {
26
+ * return window.__locatorjs__.getPath('button.submit');
27
+ * });
28
+ * console.log(path);
29
+ */
30
+ getPath(elementOrSelector: HTMLElement | string): string | null;
31
+ /**
32
+ * Get raw ancestry data for an element.
33
+ * Returns an array of objects containing component names, file paths, and line numbers.
34
+ *
35
+ * @param elementOrSelector - HTMLElement or CSS selector string
36
+ * @returns Array of ancestry items with structure:
37
+ * - elementName: HTML element tag (e.g., 'div', 'button')
38
+ * - componentName: Component name (e.g., 'LoginButton')
39
+ * - filePath: Source file path (e.g., 'src/components/LoginButton.tsx')
40
+ * - line: Line number in source file
41
+ *
42
+ * @example
43
+ * // Get structured ancestry data
44
+ * const ancestry = window.__locatorjs__.getAncestry('button.submit');
45
+ * // Returns: [
46
+ * // { elementName: 'div', componentName: 'App', filePath: 'src/App.tsx', line: 15 },
47
+ * // { elementName: 'button', componentName: 'SubmitButton', filePath: 'src/components/SubmitButton.tsx', line: 12 }
48
+ * // ]
49
+ *
50
+ * @example
51
+ * // In Playwright - extract just component names
52
+ * const components = await page.evaluate(() => {
53
+ * const ancestry = window.__locatorjs__.getAncestry('.my-element');
54
+ * return ancestry?.map(item => item.componentName).filter(Boolean);
55
+ * });
56
+ */
57
+ getAncestry(elementOrSelector: HTMLElement | string): AncestryItem[] | null;
58
+ /**
59
+ * Get both formatted path and raw ancestry data in a single call.
60
+ * Convenience method that combines getPath() and getAncestry().
61
+ *
62
+ * @param elementOrSelector - HTMLElement or CSS selector string
63
+ * @returns Object with { path: string, ancestry: AncestryItem[] }, or null
64
+ *
65
+ * @example
66
+ * // Get both formats at once
67
+ * const data = window.__locatorjs__.getPathData('button.submit');
68
+ * console.log(data.path); // Human-readable string
69
+ * console.log(data.ancestry); // Structured array
70
+ *
71
+ * @example
72
+ * // In Playwright - useful for comprehensive debugging
73
+ * const data = await page.evaluate(() => {
74
+ * return window.__locatorjs__.getPathData('.error-message');
75
+ * });
76
+ * if (data) {
77
+ * console.log('Component tree:', data.path);
78
+ * console.log('Source files:', data.ancestry.map(a => a.filePath));
79
+ * }
80
+ */
81
+ getPathData(elementOrSelector: HTMLElement | string): {
82
+ path: string;
83
+ ancestry: AncestryItem[];
84
+ } | null;
85
+ /**
86
+ * Display help information about the LocatorJS API.
87
+ * Shows usage examples and method descriptions for browser automation tools.
88
+ *
89
+ * @returns Help text as a string
90
+ *
91
+ * @example
92
+ * // View help in browser console
93
+ * console.log(window.__locatorjs__.help());
94
+ *
95
+ * @example
96
+ * // In Playwright - view help
97
+ * const help = await page.evaluate(() => window.__locatorjs__.help());
98
+ * console.log(help);
99
+ */
100
+ help(): string;
101
+ }
102
+ export declare function createBrowserAPI(adapterIdParam?: AdapterId): LocatorJSAPI;
103
+ export declare function installBrowserAPI(adapterIdParam?: AdapterId): void;
@@ -0,0 +1,160 @@
1
+ import { createTreeNode } from "./adapters/createTreeNode";
2
+ import { collectAncestry, formatAncestryChain } from "./functions/formatAncestryChain";
3
+ let adapterId;
4
+ function resolveElement(elementOrSelector) {
5
+ if (typeof elementOrSelector === "string") {
6
+ const element = document.querySelector(elementOrSelector);
7
+ return element instanceof HTMLElement ? element : null;
8
+ }
9
+ return elementOrSelector;
10
+ }
11
+ function getAncestryForElement(element) {
12
+ const treeNode = createTreeNode(element, adapterId);
13
+ if (!treeNode) {
14
+ return null;
15
+ }
16
+ return collectAncestry(treeNode);
17
+ }
18
+ const HELP_TEXT = `
19
+ ╔═══════════════════════════════════════════════════════════════════════════╗
20
+ ║ LocatorJS Browser API ║
21
+ ║ Programmatic Component Ancestry Access ║
22
+ ╚═══════════════════════════════════════════════════════════════════════════╝
23
+
24
+ METHODS:
25
+ --------
26
+
27
+ 1. getPath(elementOrSelector)
28
+ Returns a formatted string showing the component hierarchy.
29
+
30
+ Usage:
31
+ window.__locatorjs__.getPath('button.submit')
32
+ window.__locatorjs__.getPath(document.querySelector('.my-button'))
33
+
34
+ Returns:
35
+ "div in App at src/App.tsx:15
36
+ └─ button in SubmitButton at src/components/SubmitButton.tsx:12"
37
+
38
+ 2. getAncestry(elementOrSelector)
39
+ Returns raw ancestry data as an array of objects.
40
+
41
+ Usage:
42
+ window.__locatorjs__.getAncestry('button.submit')
43
+
44
+ Returns:
45
+ [
46
+ { elementName: 'div', componentName: 'App',
47
+ filePath: 'src/App.tsx', line: 15 },
48
+ { elementName: 'button', componentName: 'SubmitButton',
49
+ filePath: 'src/components/SubmitButton.tsx', line: 12 }
50
+ ]
51
+
52
+ 3. getPathData(elementOrSelector)
53
+ Returns both formatted path and raw ancestry in one call.
54
+
55
+ Usage:
56
+ const data = window.__locatorjs__.getPathData('button.submit')
57
+ console.log(data.path) // formatted string
58
+ console.log(data.ancestry) // structured array
59
+
60
+ 4. help()
61
+ Displays this help message.
62
+
63
+ PLAYWRIGHT EXAMPLES:
64
+ -------------------
65
+
66
+ // Get component path for debugging
67
+ const path = await page.evaluate(() => {
68
+ return window.__locatorjs__.getPath('button.submit');
69
+ });
70
+ console.log(path);
71
+
72
+ // Extract component names
73
+ const components = await page.evaluate(() => {
74
+ const ancestry = window.__locatorjs__.getAncestry('.error-message');
75
+ return ancestry?.map(item => item.componentName).filter(Boolean);
76
+ });
77
+
78
+ // Create a test helper
79
+ async function getComponentPath(page, selector) {
80
+ return await page.evaluate((sel) => {
81
+ return window.__locatorjs__.getPath(sel);
82
+ }, selector);
83
+ }
84
+
85
+ PUPPETEER EXAMPLES:
86
+ ------------------
87
+
88
+ const path = await page.evaluate(() => {
89
+ return window.__locatorjs__.getPath('.my-button');
90
+ });
91
+
92
+ SELENIUM EXAMPLES:
93
+ -----------------
94
+
95
+ const path = await driver.executeScript(() => {
96
+ return window.__locatorjs__.getPath('button.submit');
97
+ });
98
+
99
+ CYPRESS EXAMPLES:
100
+ ----------------
101
+
102
+ cy.window().then((win) => {
103
+ const path = win.__locatorjs__.getPath('button.submit');
104
+ cy.log(path);
105
+ });
106
+
107
+ NOTES:
108
+ ------
109
+ • Accepts CSS selectors or HTMLElement objects
110
+ • Returns null if element not found or framework not supported
111
+ • Works with React, Vue, Svelte, Preact, and any JSX framework
112
+ • Automatically installed when LocatorJS runtime initializes
113
+
114
+ Documentation: https://github.com/infi-pc/locatorjs
115
+ `;
116
+ export function createBrowserAPI(adapterIdParam) {
117
+ adapterId = adapterIdParam;
118
+ return {
119
+ getPath(elementOrSelector) {
120
+ const element = resolveElement(elementOrSelector);
121
+ if (!element) {
122
+ return null;
123
+ }
124
+ const ancestry = getAncestryForElement(element);
125
+ if (!ancestry) {
126
+ return null;
127
+ }
128
+ return formatAncestryChain(ancestry);
129
+ },
130
+ getAncestry(elementOrSelector) {
131
+ const element = resolveElement(elementOrSelector);
132
+ if (!element) {
133
+ return null;
134
+ }
135
+ return getAncestryForElement(element);
136
+ },
137
+ getPathData(elementOrSelector) {
138
+ const element = resolveElement(elementOrSelector);
139
+ if (!element) {
140
+ return null;
141
+ }
142
+ const ancestry = getAncestryForElement(element);
143
+ if (!ancestry) {
144
+ return null;
145
+ }
146
+ return {
147
+ path: formatAncestryChain(ancestry),
148
+ ancestry
149
+ };
150
+ },
151
+ help() {
152
+ return HELP_TEXT;
153
+ }
154
+ };
155
+ }
156
+ export function installBrowserAPI(adapterIdParam) {
157
+ if (typeof window !== "undefined") {
158
+ window.__locatorjs__ = createBrowserAPI(adapterIdParam);
159
+ }
160
+ }
@@ -0,0 +1,4 @@
1
+ export declare function Button(props: {
2
+ onClick: () => void;
3
+ children: any;
4
+ }): import("solid-js").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { template as _$template } from "solid-js/web";
2
+ import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
+ import { insert as _$insert } from "solid-js/web";
4
+ var _tmpl$ = /*#__PURE__*/_$template(`<button class="py-1 px-1 hover:bg-white/30 pointer hover:text-gray-100 rounded">`);
5
+ export function Button(props) {
6
+ return (() => {
7
+ var _el$ = _tmpl$();
8
+ _el$.$$click = e => {
9
+ e.preventDefault();
10
+ e.stopPropagation();
11
+ props.onClick();
12
+ };
13
+ _$insert(_el$, () => props.children);
14
+ return _el$;
15
+ })();
16
+ }
17
+ _$delegateEvents(["click"]);