@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.
- package/.eslintrc +3 -0
- package/.turbo/turbo-build.log +30 -0
- package/.turbo/turbo-test.log +18 -0
- package/.turbo/turbo-ts.log +4 -0
- package/LICENSE +22 -0
- package/babel.config.js +14 -0
- package/dist/_generated_styles.d.ts +2 -0
- package/dist/_generated_styles.js +1734 -0
- package/dist/_generated_tree_icon.d.ts +2 -0
- package/dist/_generated_tree_icon.js +2 -0
- package/dist/adapters/HtmlElementTreeNode.d.ts +16 -0
- package/dist/adapters/HtmlElementTreeNode.js +43 -0
- package/dist/adapters/adapterApi.d.ts +30 -0
- package/dist/adapters/adapterApi.js +1 -0
- package/dist/adapters/createTreeNode.d.ts +2 -0
- package/dist/adapters/createTreeNode.js +17 -0
- package/dist/adapters/getElementInfo.d.ts +2 -0
- package/dist/adapters/getElementInfo.js +19 -0
- package/dist/adapters/getParentsPath.d.ts +2 -0
- package/dist/adapters/getParentsPath.js +35 -0
- package/dist/adapters/getTree.d.ts +1 -0
- package/dist/adapters/getTree.js +35 -0
- package/dist/adapters/goUpByTheTree.d.ts +7 -0
- package/dist/adapters/goUpByTheTree.js +22 -0
- package/dist/adapters/jsx/getExpressionData.d.ts +2 -0
- package/dist/adapters/jsx/getExpressionData.js +44 -0
- package/dist/adapters/jsx/getJSXComponentBoundingBox.d.ts +5 -0
- package/dist/adapters/jsx/getJSXComponentBoundingBox.js +46 -0
- package/dist/adapters/jsx/jsxAdapter.d.ts +11 -0
- package/dist/adapters/jsx/jsxAdapter.js +208 -0
- package/dist/adapters/jsx/runtimeStore.d.ts +10 -0
- package/dist/adapters/jsx/runtimeStore.js +87 -0
- package/dist/adapters/react/fiberToSimple.d.ts +3 -0
- package/dist/adapters/react/fiberToSimple.js +55 -0
- package/dist/adapters/react/findDebugSource.d.ts +5 -0
- package/dist/adapters/react/findDebugSource.js +13 -0
- package/dist/adapters/react/findFiberByHtmlElement.d.ts +2 -0
- package/dist/adapters/react/findFiberByHtmlElement.js +22 -0
- package/dist/adapters/react/gatherFiberRoots.d.ts +2 -0
- package/dist/adapters/react/gatherFiberRoots.js +29 -0
- package/dist/adapters/react/getAllFiberChildren.d.ts +2 -0
- package/dist/adapters/react/getAllFiberChildren.js +9 -0
- package/dist/adapters/react/getAllParentsElementsAndRootComponent.d.ts +8 -0
- package/dist/adapters/react/getAllParentsElementsAndRootComponent.js +34 -0
- package/dist/adapters/react/getAllWrappingParents.d.ts +2 -0
- package/dist/adapters/react/getAllWrappingParents.js +19 -0
- package/dist/adapters/react/getFiberComponentBoundingBox.d.ts +3 -0
- package/dist/adapters/react/getFiberComponentBoundingBox.js +27 -0
- package/dist/adapters/react/getFiberLabel.d.ts +3 -0
- package/dist/adapters/react/getFiberLabel.js +14 -0
- package/dist/adapters/react/getFiberOwnBoundingBox.d.ts +3 -0
- package/dist/adapters/react/getFiberOwnBoundingBox.js +6 -0
- package/dist/adapters/react/isStyled.d.ts +2 -0
- package/dist/adapters/react/isStyled.js +3 -0
- package/dist/adapters/react/makeFiberId.d.ts +2 -0
- package/dist/adapters/react/makeFiberId.js +16 -0
- package/dist/adapters/react/reactAdapter.d.ts +11 -0
- package/dist/adapters/react/reactAdapter.js +114 -0
- package/dist/adapters/react/searchDevtoolsRenderersForClosestTarget.d.ts +1 -0
- package/dist/adapters/react/searchDevtoolsRenderersForClosestTarget.js +11 -0
- package/dist/adapters/svelte/svelteAdapter.d.ts +22 -0
- package/dist/adapters/svelte/svelteAdapter.js +88 -0
- package/dist/adapters/vue/getVNodeBoundingBox.d.ts +4 -0
- package/dist/adapters/vue/getVNodeBoundingBox.js +31 -0
- package/dist/adapters/vue/vueAdapter.d.ts +15 -0
- package/dist/adapters/vue/vueAdapter.js +113 -0
- package/dist/browserApi.d.ts +103 -0
- package/dist/browserApi.js +160 -0
- package/dist/components/Button.d.ts +4 -0
- package/dist/components/Button.js +17 -0
- package/dist/components/ComponentOutline.d.ts +7 -0
- package/dist/components/ComponentOutline.js +93 -0
- package/dist/components/MaybeOutline.d.ts +7 -0
- package/dist/components/MaybeOutline.js +43 -0
- package/dist/components/Outline.d.ts +25 -0
- package/dist/components/Outline.js +135 -0
- package/dist/components/RenderBoxes.d.ts +4 -0
- package/dist/components/RenderBoxes.js +73 -0
- package/dist/components/Runtime.d.ts +3 -0
- package/dist/components/Runtime.js +188 -0
- package/dist/components/SimpleNodeOutline.d.ts +4 -0
- package/dist/components/SimpleNodeOutline.js +47 -0
- package/dist/components/Toast.d.ts +4 -0
- package/dist/components/Toast.js +68 -0
- package/dist/components/Tooltip.d.ts +5 -0
- package/dist/components/Tooltip.js +21 -0
- package/dist/consts.d.ts +6 -0
- package/dist/consts.js +5 -0
- package/dist/functions/cropPath.d.ts +1 -0
- package/dist/functions/cropPath.js +9 -0
- package/dist/functions/cropPath.test.d.ts +1 -0
- package/dist/functions/cropPath.test.js +16 -0
- package/dist/functions/deduplicateLabels.d.ts +2 -0
- package/dist/functions/deduplicateLabels.js +12 -0
- package/dist/functions/evalTemplate.d.ts +3 -0
- package/dist/functions/evalTemplate.js +7 -0
- package/dist/functions/evalTemplate.test.d.ts +1 -0
- package/dist/functions/evalTemplate.test.js +11 -0
- package/dist/functions/findNames.d.ts +5 -0
- package/dist/functions/findNames.js +15 -0
- package/dist/functions/formatAncestryChain.d.ts +9 -0
- package/dist/functions/formatAncestryChain.js +56 -0
- package/dist/functions/getBoundingRect.d.ts +1 -0
- package/dist/functions/getBoundingRect.js +11 -0
- package/dist/functions/getComposedBoundingBox.d.ts +2 -0
- package/dist/functions/getComposedBoundingBox.js +20 -0
- package/dist/functions/getIdsOnPathToRoot.d.ts +3 -0
- package/dist/functions/getIdsOnPathToRoot.js +15 -0
- package/dist/functions/getMultipleElementsBoundingBox.d.ts +2 -0
- package/dist/functions/getMultipleElementsBoundingBox.js +20 -0
- package/dist/functions/getPathToParent.d.ts +1 -0
- package/dist/functions/getPathToParent.js +15 -0
- package/dist/functions/getReferenceId.d.ts +1 -0
- package/dist/functions/getReferenceId.js +9 -0
- package/dist/functions/getUsableFileName.d.ts +1 -0
- package/dist/functions/getUsableFileName.js +17 -0
- package/dist/functions/getUsableFileName.test.d.ts +1 -0
- package/dist/functions/getUsableFileName.test.js +16 -0
- package/dist/functions/getUsableName.d.ts +2 -0
- package/dist/functions/getUsableName.js +47 -0
- package/dist/functions/isCombinationModifiersPressed.d.ts +4 -0
- package/dist/functions/isCombinationModifiersPressed.js +16 -0
- package/dist/functions/isLocatorsOwnElement.d.ts +1 -0
- package/dist/functions/isLocatorsOwnElement.js +3 -0
- package/dist/functions/mergeRects.d.ts +2 -0
- package/dist/functions/mergeRects.js +10 -0
- package/dist/functions/mergeRects.test.d.ts +1 -0
- package/dist/functions/mergeRects.test.js +23 -0
- package/dist/functions/nonNullable.d.ts +1 -0
- package/dist/functions/nonNullable.js +3 -0
- package/dist/functions/parseDataId.d.ts +3 -0
- package/dist/functions/parseDataId.js +44 -0
- package/dist/functions/transformPath.d.ts +1 -0
- package/dist/functions/transformPath.js +7 -0
- package/dist/functions/transformPath.test.d.ts +1 -0
- package/dist/functions/transformPath.test.js +16 -0
- package/dist/global.d.js +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +13 -0
- package/dist/initRuntime.d.ts +8 -0
- package/dist/initRuntime.js +80 -0
- package/dist/output.css +1733 -0
- package/dist/types/LabelData.d.ts +5 -0
- package/dist/types/LabelData.js +1 -0
- package/dist/types/TreeNode.d.ts +19 -0
- package/dist/types/TreeNode.js +1 -0
- package/dist/types/types.d.ts +53 -0
- package/dist/types/types.js +1 -0
- package/jest.config.ts +195 -0
- package/package.json +75 -0
- package/scripts/wrapCSS.js +26 -0
- package/scripts/wrapImage.js +24 -0
- package/src/_generated_styles.ts +1734 -0
- package/src/_generated_tree_icon.ts +2 -0
- package/src/adapters/HtmlElementTreeNode.ts +51 -0
- package/src/adapters/adapterApi.ts +35 -0
- package/src/adapters/createTreeNode.ts +25 -0
- package/src/adapters/getElementInfo.tsx +27 -0
- package/src/adapters/getParentsPath.tsx +49 -0
- package/src/adapters/getTree.tsx +45 -0
- package/src/adapters/goUpByTheTree.ts +20 -0
- package/src/adapters/jsx/getExpressionData.ts +47 -0
- package/src/adapters/jsx/getJSXComponentBoundingBox.ts +63 -0
- package/src/adapters/jsx/jsxAdapter.ts +276 -0
- package/src/adapters/jsx/runtimeStore.ts +94 -0
- package/src/adapters/react/fiberToSimple.tsx +72 -0
- package/src/adapters/react/findDebugSource.ts +15 -0
- package/src/adapters/react/findFiberByHtmlElement.ts +27 -0
- package/src/adapters/react/gatherFiberRoots.tsx +36 -0
- package/src/adapters/react/getAllFiberChildren.tsx +11 -0
- package/src/adapters/react/getAllParentsElementsAndRootComponent.ts +52 -0
- package/src/adapters/react/getAllWrappingParents.ts +25 -0
- package/src/adapters/react/getFiberComponentBoundingBox.ts +30 -0
- package/src/adapters/react/getFiberLabel.ts +20 -0
- package/src/adapters/react/getFiberOwnBoundingBox.ts +9 -0
- package/src/adapters/react/isStyled.ts +5 -0
- package/src/adapters/react/makeFiberId.tsx +19 -0
- package/src/adapters/react/reactAdapter.ts +148 -0
- package/src/adapters/react/searchDevtoolsRenderersForClosestTarget.ts +15 -0
- package/src/adapters/svelte/svelteAdapter.ts +111 -0
- package/src/adapters/vue/getVNodeBoundingBox.tsx +42 -0
- package/src/adapters/vue/vueAdapter.ts +139 -0
- package/src/assets/tree-icon.png +0 -0
- package/src/browserApi.ts +288 -0
- package/src/components/Button.tsx +14 -0
- package/src/components/ComponentOutline.tsx +98 -0
- package/src/components/MaybeOutline.tsx +49 -0
- package/src/components/Outline.tsx +153 -0
- package/src/components/RenderBoxes.tsx +57 -0
- package/src/components/Runtime.tsx +246 -0
- package/src/components/SimpleNodeOutline.tsx +27 -0
- package/src/components/Toast.tsx +83 -0
- package/src/components/Tooltip.tsx +28 -0
- package/src/consts.ts +7 -0
- package/src/functions/cropPath.test.ts +18 -0
- package/src/functions/cropPath.ts +12 -0
- package/src/functions/deduplicateLabels.ts +16 -0
- package/src/functions/evalTemplate.test.ts +12 -0
- package/src/functions/evalTemplate.ts +8 -0
- package/src/functions/findNames.ts +20 -0
- package/src/functions/formatAncestryChain.ts +80 -0
- package/src/functions/getBoundingRect.tsx +11 -0
- package/src/functions/getComposedBoundingBox.tsx +25 -0
- package/src/functions/getIdsOnPathToRoot.tsx +21 -0
- package/src/functions/getMultipleElementsBoundingBox.tsx +25 -0
- package/src/functions/getPathToParent.tsx +17 -0
- package/src/functions/getReferenceId.tsx +10 -0
- package/src/functions/getUsableFileName.test.tsx +24 -0
- package/src/functions/getUsableFileName.tsx +19 -0
- package/src/functions/getUsableName.ts +52 -0
- package/src/functions/isCombinationModifiersPressed.ts +32 -0
- package/src/functions/isLocatorsOwnElement.tsx +9 -0
- package/src/functions/mergeRects.test.ts +15 -0
- package/src/functions/mergeRects.tsx +12 -0
- package/src/functions/nonNullable.ts +3 -0
- package/src/functions/parseDataId.ts +62 -0
- package/src/functions/transformPath.test.ts +28 -0
- package/src/functions/transformPath.ts +7 -0
- package/src/global.d.ts +31 -0
- package/src/index.ts +18 -0
- package/src/initRuntime.ts +83 -0
- package/src/main.css +3 -0
- package/src/types/LabelData.ts +6 -0
- package/src/types/TreeNode.ts +22 -0
- package/src/types/types.ts +55 -0
- package/tailwind.config.js +9 -0
- 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,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
|
+
});
|
package/src/global.d.ts
ADDED
|
@@ -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,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 };
|
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
|
+
}
|