@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,24 @@
1
+ import getUsableFileName from "./getUsableFileName"; // assuming the function is in filenameUtils.ts
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ describe("getUsableFileName", () => {
5
+ it("should return filename if it is not index.<extension>", () => {
6
+ expect(getUsableFileName("myProject/utils/getSomething.js")).toBe(
7
+ "getSomething.js"
8
+ );
9
+ });
10
+
11
+ it("should return parent folder and filename if the filename is index.<extension>", () => {
12
+ expect(getUsableFileName("myProject/Button/index.tsx")).toBe(
13
+ "Button/index.tsx"
14
+ );
15
+ });
16
+
17
+ it("should handle paths with only one segment", () => {
18
+ expect(getUsableFileName("file.ts")).toBe("file.ts");
19
+ });
20
+
21
+ it("should handle paths with index.<extension> without parent folder", () => {
22
+ expect(getUsableFileName("index.ts")).toBe("index.ts");
23
+ });
24
+ });
@@ -0,0 +1,19 @@
1
+ export default function getUsableFileName(path: string): string {
2
+ const pathSegments = path.split("/");
3
+
4
+ // If the path has only one segment, return it directly.
5
+ if (pathSegments.length === 1) {
6
+ return pathSegments[0] || "unnamed";
7
+ }
8
+
9
+ const fileName = pathSegments[pathSegments.length - 1] || "unnamed";
10
+
11
+ if (fileName.startsWith("index.")) {
12
+ // If the file name is index.<extension>, return the parent folder's name
13
+ // along with the filename.
14
+ return `${pathSegments[pathSegments.length - 2]}/${fileName}`;
15
+ } else {
16
+ // Otherwise, just return the filename.
17
+ return fileName;
18
+ }
19
+ }
@@ -0,0 +1,52 @@
1
+ import { Fiber } from "@locator/shared";
2
+
3
+ // function printDebugOwnerTree(fiber: Fiber): string | null {
4
+ // let current: Fiber | null = fiber || null;
5
+ // let results = [];
6
+ // while (current) {
7
+ // results.push(getUsableName(current));
8
+ // current = current._debugOwner || null;
9
+ // }
10
+ // console.log('DEBUG OWNER: ', results);
11
+ // return null;
12
+ // }
13
+ // function printReturnTree(fiber: Fiber): string | null {
14
+ // let current: Fiber | null = fiber || null;
15
+ // let results = [];
16
+ // while (current) {
17
+ // results.push(getUsableName(current));
18
+ // current = current.return || null;
19
+ // }
20
+ // console.log('RETURN: ', results);
21
+ // return null;
22
+ // }
23
+
24
+ export function getUsableName(fiber: Fiber | null | undefined): string {
25
+ if (!fiber) {
26
+ return "Not found";
27
+ }
28
+
29
+ if (typeof fiber.elementType === "string") {
30
+ return fiber.elementType;
31
+ }
32
+ if (!fiber.elementType) {
33
+ return "Anonymous";
34
+ }
35
+
36
+ if (fiber.elementType.name) {
37
+ return fiber.elementType.name;
38
+ }
39
+ // Not sure about this
40
+ if (fiber.elementType.displayName) {
41
+ return fiber.elementType.displayName;
42
+ }
43
+ // Used in rect.memo
44
+ if (fiber.elementType.type?.name) {
45
+ return fiber.elementType.type.name;
46
+ }
47
+ if (fiber.elementType._payload?._result?.name) {
48
+ return fiber.elementType._payload._result.name;
49
+ }
50
+
51
+ return "Anonymous";
52
+ }
@@ -0,0 +1,32 @@
1
+ export function getMouseModifiers() {
2
+ const mouseModifiers =
3
+ document.documentElement.dataset.locatorMouseModifiers || "alt";
4
+ const mouseModifiersArray = mouseModifiers.split("+");
5
+ const modifiers: { [key: string]: true } = {};
6
+ mouseModifiersArray.forEach((modifier) => {
7
+ modifiers[modifier] = true;
8
+ }, {});
9
+
10
+ return modifiers;
11
+ }
12
+
13
+ export function isCombinationModifiersPressed(
14
+ e: MouseEvent | KeyboardEvent,
15
+ rightClick = false
16
+ ) {
17
+ const modifiers = getMouseModifiers();
18
+
19
+ if (rightClick) {
20
+ return (
21
+ e.altKey == !!modifiers.alt &&
22
+ e.metaKey == !!modifiers.meta &&
23
+ e.shiftKey == !!modifiers.shift
24
+ );
25
+ }
26
+ return (
27
+ e.altKey == !!modifiers.alt &&
28
+ e.ctrlKey == !!modifiers.ctrl &&
29
+ e.metaKey == !!modifiers.meta &&
30
+ e.shiftKey == !!modifiers.shift
31
+ );
32
+ }
@@ -0,0 +1,9 @@
1
+ export function isLocatorsOwnElement(element: HTMLElement) {
2
+ return (
3
+ element.className == "locatorjs-label" ||
4
+ element.id == "locatorjs-labels-section" ||
5
+ element.id == "locatorjs-layer" ||
6
+ element.id == "locatorjs-wrapper" ||
7
+ element.matches("#locatorjs-wrapper *")
8
+ );
9
+ }
@@ -0,0 +1,15 @@
1
+ import { mergeRects } from "./mergeRects";
2
+ import { SimpleDOMRect } from "../types/types";
3
+ import { describe, expect, test } from "vitest";
4
+
5
+ describe("mergeRects", () => {
6
+ test("basic", () => {
7
+ const a: SimpleDOMRect = { x: 0, y: 0, width: 10, height: 10 };
8
+ const b: SimpleDOMRect = { x: 5, y: 5, width: 10, height: 10 };
9
+ const res = mergeRects(a, b);
10
+ expect(res.x).toEqual(0);
11
+ expect(res.y).toEqual(0);
12
+ expect(res.width).toEqual(15);
13
+ expect(res.height).toEqual(15);
14
+ });
15
+ });
@@ -0,0 +1,12 @@
1
+ import { SimpleDOMRect } from "../types/types";
2
+
3
+ export function mergeRects(a: SimpleDOMRect, b: SimpleDOMRect): SimpleDOMRect {
4
+ const x = Math.min(a.x, b.x);
5
+ const y = Math.min(a.y, b.y);
6
+ return {
7
+ x,
8
+ y,
9
+ width: Math.max(a.x + a.width, b.x + b.width) - x,
10
+ height: Math.max(a.y + a.height, b.y + b.height) - y,
11
+ };
12
+ }
@@ -0,0 +1,3 @@
1
+ export default function nonNullable<T>(value: T): value is NonNullable<T> {
2
+ return value !== null && value !== undefined;
3
+ }
@@ -0,0 +1,62 @@
1
+ export function parseDataId(
2
+ dataId: string
3
+ ): [fileFullPath: string, id: string] {
4
+ const [fileFullPath, id] = dataId.split("::");
5
+ if (!fileFullPath || !id) {
6
+ throw new Error("locatorjsId is malformed");
7
+ }
8
+ return [fileFullPath, id];
9
+ }
10
+
11
+ export function parseDataPath(
12
+ dataPath: string
13
+ ): [fileFullPath: string, line: number, column: number] | null {
14
+ // Format: /path/to/file.tsx:line:column
15
+ // Need to handle Windows paths like C:\path\to\file.tsx:line:column
16
+
17
+ // Find the last two colons (for line and column)
18
+ const lastColonIndex = dataPath.lastIndexOf(":");
19
+ if (lastColonIndex === -1) return null;
20
+
21
+ const secondLastColonIndex = dataPath.lastIndexOf(":", lastColonIndex - 1);
22
+ if (secondLastColonIndex === -1) return null;
23
+
24
+ const fileFullPath = dataPath.substring(0, secondLastColonIndex);
25
+ const lineStr = dataPath.substring(secondLastColonIndex + 1, lastColonIndex);
26
+ const columnStr = dataPath.substring(lastColonIndex + 1);
27
+
28
+ const line = parseInt(lineStr, 10);
29
+ const column = parseInt(columnStr, 10);
30
+
31
+ if (Number.isNaN(line) || Number.isNaN(column)) {
32
+ return null;
33
+ }
34
+
35
+ return [fileFullPath, line, column];
36
+ }
37
+
38
+ export function splitFullPath(
39
+ fullPath: string
40
+ ): [projectPath: string, filePath: string] {
41
+ // Try to find a common project root indicator
42
+ const indicators = ["/src/", "/app/", "/pages/", "/components/"];
43
+
44
+ for (const indicator of indicators) {
45
+ const index = fullPath.indexOf(indicator);
46
+ if (index !== -1) {
47
+ return [fullPath.substring(0, index), fullPath.substring(index)];
48
+ }
49
+ }
50
+
51
+ // Fallback: treat the whole path as project path with empty file path
52
+ // or try to split at the last reasonable directory
53
+ const lastSlash = fullPath.lastIndexOf("/");
54
+ if (lastSlash !== -1) {
55
+ return [
56
+ fullPath.substring(0, lastSlash + 1),
57
+ fullPath.substring(lastSlash + 1),
58
+ ];
59
+ }
60
+
61
+ return [fullPath, ""];
62
+ }
@@ -0,0 +1,28 @@
1
+ import { transformPath } from "./transformPath";
2
+ import { describe, expect, test } from "vitest";
3
+
4
+ describe("transformPath", () => {
5
+ test("replacing internal url to external", () => {
6
+ expect(transformPath("/app/src/myFile.js", "/app/", "C://myPath/")).toBe(
7
+ "C://myPath/src/myFile.js"
8
+ );
9
+ });
10
+
11
+ test("unifying absolute and relative paths", () => {
12
+ const from = "^/src/";
13
+ const to = "/myPath/src/";
14
+
15
+ expect(transformPath("/src/myFile.js", from, to)).toBe(
16
+ "/myPath/src/myFile.js"
17
+ );
18
+ expect(transformPath("/myPath/src/myFile.js", from, to)).toBe(
19
+ "/myPath/src/myFile.js"
20
+ );
21
+ });
22
+
23
+ test("invalid regex should keep original", () => {
24
+ expect(transformPath("/app/src/myFile.js", "[", "C://myPath/")).toBe(
25
+ "/app/src/myFile.js"
26
+ );
27
+ });
28
+ });
@@ -0,0 +1,7 @@
1
+ export function transformPath(path: string, from: string, to: string) {
2
+ try {
3
+ return path.replace(new RegExp(`${from}`), to);
4
+ } catch (e) {
5
+ return path;
6
+ }
7
+ }
@@ -0,0 +1,31 @@
1
+ import { ReactDevtoolsHook } from "@locator/shared";
2
+ import { FileStorage } from "./types/types";
3
+ import { LocatorJSAPI } from "./browserApi";
4
+
5
+ declare global {
6
+ interface Window {
7
+ __REACT_DEVTOOLS_GLOBAL_HOOK__: ReactDevtoolsHook;
8
+ __LOCATOR_DATA__: { [filename: string]: FileStorage };
9
+ /**
10
+ * LocatorJS Browser API
11
+ *
12
+ * Provides programmatic access to component ancestry information.
13
+ * Works with browser automation tools like Playwright, Puppeteer, Selenium, etc.
14
+ *
15
+ * @example
16
+ * // In Playwright
17
+ * const path = await page.evaluate(() => {
18
+ * return window.__locatorjs__.getPath('button.submit');
19
+ * });
20
+ * console.log(path);
21
+ *
22
+ * @example
23
+ * // Get raw ancestry data
24
+ * const ancestry = await page.evaluate(() => {
25
+ * const element = document.querySelector('button.submit');
26
+ * return window.__locatorjs__.getAncestry(element);
27
+ * });
28
+ */
29
+ __locatorjs__: LocatorJSAPI;
30
+ }
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { Target } from "@locator/shared";
2
+ import { AdapterId } from "./consts";
3
+ import { initRuntime } from "./initRuntime";
4
+ export * from "./adapters/jsx/runtimeStore";
5
+
6
+ export const MAX_ZINDEX = 2147483647;
7
+
8
+ export function setup({
9
+ adapter,
10
+ targets,
11
+ }: {
12
+ adapter?: AdapterId;
13
+ targets?: { [k: string]: Target | string };
14
+ } = {}) {
15
+ setTimeout(() => initRuntime({ adapter, targets }), 0);
16
+ }
17
+
18
+ export default setup;
@@ -0,0 +1,83 @@
1
+ import { allTargets, Target } from "@locator/shared";
2
+ import { AdapterId, fontFamily } from "./consts";
3
+ import generatedStyles from "./_generated_styles";
4
+ import { MAX_ZINDEX } from "./index";
5
+ import { installBrowserAPI } from "./browserApi";
6
+
7
+ export function initRuntime({
8
+ adapter,
9
+ targets,
10
+ }: {
11
+ adapter?: AdapterId;
12
+ targets?: { [k: string]: Target | string };
13
+ } = {}) {
14
+ if (typeof window === "undefined" || typeof document === "undefined") {
15
+ return;
16
+ }
17
+ if (document.getElementById("locatorjs-wrapper")) {
18
+ // already initialized
19
+ return;
20
+ }
21
+
22
+ // Install browser API on window.__locatorjs__
23
+ installBrowserAPI(adapter);
24
+
25
+ // add style tag to head
26
+ const style = document.createElement("style");
27
+ style.id = "locatorjs-style";
28
+ style.innerHTML = `
29
+ #locatorjs-layer {
30
+ all: initial;
31
+ pointer-events: none;
32
+ font-family: ${fontFamily};
33
+ }
34
+ #locatorjs-layer * {
35
+ box-sizing: border-box;
36
+ }
37
+ #locatorjs-labels-wrapper {
38
+ display: flex;
39
+ gap: 8px;
40
+ }
41
+ ${generatedStyles}
42
+ `;
43
+
44
+ const globalStyle = document.createElement("style");
45
+ globalStyle.id = "locatorjs-global-style";
46
+ globalStyle.innerHTML = `
47
+ #locatorjs-wrapper {
48
+ z-index: ${MAX_ZINDEX};
49
+ pointer-events: none;
50
+ position: fixed;
51
+ }
52
+ .locatorjs-active-pointer * {
53
+ cursor: pointer !important;
54
+ }
55
+ `;
56
+
57
+ const wrapper = document.createElement("div");
58
+ wrapper.setAttribute("id", "locatorjs-wrapper");
59
+
60
+ const shadow = wrapper.attachShadow({ mode: "open" });
61
+ const layer = document.createElement("div");
62
+ layer.setAttribute("id", "locatorjs-layer");
63
+
64
+ shadow.appendChild(style);
65
+ shadow.appendChild(layer);
66
+
67
+ document.body.appendChild(wrapper);
68
+ document.head.appendChild(globalStyle);
69
+
70
+ // This weird import is needed because:
71
+ // SSR React (Next.js) breaks when importing any SolidJS compiled file, so the import has to be conditional
72
+ // Browser Extension breaks when importing with "import()"
73
+ // Vite breaks when importing with "require()"
74
+ if (typeof require !== "undefined") {
75
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
76
+ const { initRender } = require("./components/Runtime");
77
+ initRender(layer, adapter, targets || allTargets);
78
+ } else {
79
+ import("./components/Runtime").then(({ initRender }) => {
80
+ initRender(layer, adapter, targets || allTargets);
81
+ });
82
+ }
83
+ }
package/src/main.css ADDED
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1,6 @@
1
+ import { LinkProps } from "./types";
2
+
3
+ export type LabelData = {
4
+ link: LinkProps | null;
5
+ label: string;
6
+ };
@@ -0,0 +1,22 @@
1
+ import { SimpleDOMRect, Source } from "./types";
2
+
3
+ export interface TreeNode {
4
+ type: "component" | "element";
5
+ name: string;
6
+ uniqueId: string;
7
+ getBox(): SimpleDOMRect | null;
8
+ getParent(): TreeNode | null;
9
+ getChildren(): TreeNode[];
10
+ getSource(): Source | null;
11
+ getComponent(): TreeNodeComponent | null;
12
+ }
13
+
14
+ export type TreeNodeComponent = {
15
+ label: string;
16
+ callLink?: Source;
17
+ definitionLink?: Source;
18
+ };
19
+
20
+ export interface TreeNodeElement extends TreeNode {
21
+ getElement(): Element | Text;
22
+ }
@@ -0,0 +1,55 @@
1
+ import { Fiber, Target } from "@locator/shared";
2
+
3
+ export type Source = {
4
+ fileName: string;
5
+ lineNumber: number;
6
+ columnNumber?: number;
7
+ projectPath?: string;
8
+ };
9
+
10
+ type SimpleElement = {
11
+ type: "element";
12
+ name: string;
13
+ uniqueId: string;
14
+ fiber: Fiber;
15
+ box: SimpleDOMRect | null;
16
+ element: Element | Text;
17
+ children: (SimpleElement | SimpleComponent)[];
18
+ source: Source | null;
19
+ };
20
+
21
+ type SimpleComponent = {
22
+ type: "component";
23
+ uniqueId: string;
24
+ name: string;
25
+ fiber: Fiber;
26
+ box: SimpleDOMRect | null;
27
+ children: (SimpleElement | SimpleComponent)[];
28
+ source: Source | null;
29
+ definitionSourceFile: string | null;
30
+ };
31
+
32
+ export type SimpleNode = SimpleElement | SimpleComponent;
33
+
34
+ export type HighlightedNode = {
35
+ getNode: () => SimpleNode | null;
36
+ setNode: (node: SimpleNode | null) => void;
37
+ };
38
+
39
+ export type SimpleDOMRect = {
40
+ height: number;
41
+ width: number;
42
+ x: number;
43
+ y: number;
44
+ };
45
+
46
+ export type Targets = { [k: string]: Target | string };
47
+
48
+ export type LinkProps = {
49
+ filePath: string;
50
+ projectPath: string;
51
+ line: number;
52
+ column: number;
53
+ };
54
+
55
+ export type ContextMenuState = { target: HTMLElement; x: number; y: number };
@@ -0,0 +1,9 @@
1
+ /* eslint-disable no-undef */
2
+ /** @type {import('tailwindcss').Config} */
3
+ module.exports = {
4
+ content: ["./src/**/*.{tsx,ts}"],
5
+ theme: {
6
+ extend: {},
7
+ },
8
+ plugins: [require("@tailwindcss/forms")],
9
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "include": ["src"],
3
+ "exclude": ["dist", "build", "node_modules"],
4
+ "compilerOptions": {
5
+ "module": "commonjs",
6
+ "target": "es6",
7
+ "noEmit": true,
8
+ "jsx": "preserve",
9
+ "jsxImportSource": "solid-js",
10
+ "lib": ["ESNext", "dom", "DOM.Iterable"],
11
+ "moduleResolution": "node",
12
+ "forceConsistentCasingInFileNames": true,
13
+ "strict": true,
14
+ "noUncheckedIndexedAccess": true,
15
+ "strictNullChecks": true,
16
+ "noImplicitAny": true,
17
+ "esModuleInterop": true,
18
+ "skipLibCheck": true
19
+ }
20
+ }