Sign In

@treelocator/runtime

Package Overview
Dependencies
Maintainers
2
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@treelocator/runtime - npm Package Compare versions

Comparing version
0.4.6
to
0.4.7
+3
-2
dist/functions/formatAncestryChain.d.ts

@@ -23,4 +23,5 @@ import { TreeNode } from "../types/TreeNode";

* Truncate ancestry to keep only the local context.
* - If the clicked element has no filePath: keep up to the first ancestor with a file.
* - If the clicked element has a filePath: keep up to the first ancestor with a DIFFERENT file.
* - If the clicked element has no file: keep up to the first ancestor with a file.
* - If the clicked element has a file: keep up to the first ancestor with a DIFFERENT file.
* Checks both client filePath and serverComponents for file info.
* The ancestry array is bottom-up: index 0 = clicked element, last = root.

@@ -27,0 +28,0 @@ */

+21
-5

@@ -126,5 +126,20 @@ import { parsePhoenixServerComponents } from "../adapters/phoenix/parsePhoenixComments";

/**
* Get the effective file path from an AncestryItem, checking both
* client filePath and serverComponents (Next.js RSC, Phoenix, etc.).
*/
function getItemFilePath(item) {
if (item.filePath) return item.filePath;
if (item.serverComponents && item.serverComponents.length > 0) {
const comp = item.serverComponents.find(sc => sc.type === "component");
if (comp) return comp.filePath;
return item.serverComponents[0].filePath;
}
return undefined;
}
/**
* Truncate ancestry to keep only the local context.
* - If the clicked element has no filePath: keep up to the first ancestor with a file.
* - If the clicked element has a filePath: keep up to the first ancestor with a DIFFERENT file.
* - If the clicked element has no file: keep up to the first ancestor with a file.
* - If the clicked element has a file: keep up to the first ancestor with a DIFFERENT file.
* Checks both client filePath and serverComponents for file info.
* The ancestry array is bottom-up: index 0 = clicked element, last = root.

@@ -134,6 +149,6 @@ */

if (items.length === 0) return items;
const clickedFile = items[0]?.filePath;
const clickedFile = getItemFilePath(items[0]);
if (!clickedFile) {
// Clicked element has no file: find first ancestor with any file
const firstWithFile = items.findIndex(item => item.filePath);
const firstWithFile = items.findIndex(item => getItemFilePath(item));
if (firstWithFile === -1) return items;

@@ -145,3 +160,4 @@ return items.slice(0, firstWithFile + 1);

for (let i = 1; i < items.length; i++) {
if (items[i].filePath && items[i].filePath !== clickedFile) {
const ancestorFile = getItemFilePath(items[i]);
if (ancestorFile && ancestorFile !== clickedFile) {
return items.slice(0, i + 1);

@@ -148,0 +164,0 @@ }

@@ -370,2 +370,60 @@ import { describe, it, expect } from "vitest";

});
it("uses serverComponents file path when filePath is missing", () => {
const items = [{
elementName: "div",
componentName: "TurnActivityBox",
serverComponents: [{
name: "TurnActivityBox",
filePath: "components/MessageRow.tsx",
line: 921,
type: "component"
}]
}, {
elementName: "div",
componentName: "MessageRow",
serverComponents: [{
name: "MessageRow",
filePath: "components/chat/ChatViewport.tsx",
line: 917,
type: "component"
}]
}, {
elementName: "main",
componentName: "Home",
serverComponents: [{
name: "Home",
filePath: "app/page.tsx",
line: 817,
type: "component"
}]
}];
const result = truncateAtFirstFile(items);
expect(result).toEqual([items[0], items[1]]);
});
it("uses serverComponents when clicked element has no filePath but has serverComponents", () => {
const items = [{
elementName: "span",
componentName: "Button"
}, {
elementName: "div",
componentName: "Card",
serverComponents: [{
name: "Card",
filePath: "src/Card.tsx",
line: 10,
type: "component"
}]
}, {
elementName: "div",
componentName: "App",
serverComponents: [{
name: "App",
filePath: "src/App.tsx",
line: 1,
type: "component"
}]
}];
const result = truncateAtFirstFile(items);
expect(result).toEqual([items[0], items[1]]);
});
it("returns empty array for empty input", () => {

@@ -372,0 +430,0 @@ expect(truncateAtFirstFile([])).toEqual([]);

{
"name": "@treelocator/runtime",
"version": "0.4.6",
"version": "0.4.7",
"description": "TreeLocatorJS runtime for component ancestry tracking. Alt+click any element to copy its component tree to clipboard. Exposes window.__treelocator__ API for browser automation (Playwright, Puppeteer, Selenium, Cypress).",

@@ -5,0 +5,0 @@ "keywords": [

@@ -339,2 +339,30 @@ import { describe, it, expect } from "vitest";

it("uses serverComponents file path when filePath is missing", () => {
const items: AncestryItem[] = [
{ elementName: "div", componentName: "TurnActivityBox", serverComponents: [{ name: "TurnActivityBox", filePath: "components/MessageRow.tsx", line: 921, type: "component" }] },
{ elementName: "div", componentName: "MessageRow", serverComponents: [{ name: "MessageRow", filePath: "components/chat/ChatViewport.tsx", line: 917, type: "component" }] },
{ elementName: "main", componentName: "Home", serverComponents: [{ name: "Home", filePath: "app/page.tsx", line: 817, type: "component" }] },
];
const result = truncateAtFirstFile(items);
expect(result).toEqual([
items[0],
items[1],
]);
});
it("uses serverComponents when clicked element has no filePath but has serverComponents", () => {
const items: AncestryItem[] = [
{ elementName: "span", componentName: "Button" },
{ elementName: "div", componentName: "Card", serverComponents: [{ name: "Card", filePath: "src/Card.tsx", line: 10, type: "component" }] },
{ elementName: "div", componentName: "App", serverComponents: [{ name: "App", filePath: "src/App.tsx", line: 1, type: "component" }] },
];
const result = truncateAtFirstFile(items);
expect(result).toEqual([
items[0],
items[1],
]);
});
it("returns empty array for empty input", () => {

@@ -341,0 +369,0 @@ expect(truncateAtFirstFile([])).toEqual([]);

@@ -168,5 +168,20 @@ import { TreeNode, TreeNodeComponent, TreeNodeElement } from "../types/TreeNode";

/**
* Get the effective file path from an AncestryItem, checking both
* client filePath and serverComponents (Next.js RSC, Phoenix, etc.).
*/
function getItemFilePath(item: AncestryItem): string | undefined {
if (item.filePath) return item.filePath;
if (item.serverComponents && item.serverComponents.length > 0) {
const comp = item.serverComponents.find((sc) => sc.type === "component");
if (comp) return comp.filePath;
return item.serverComponents[0]!.filePath;
}
return undefined;
}
/**
* Truncate ancestry to keep only the local context.
* - If the clicked element has no filePath: keep up to the first ancestor with a file.
* - If the clicked element has a filePath: keep up to the first ancestor with a DIFFERENT file.
* - If the clicked element has no file: keep up to the first ancestor with a file.
* - If the clicked element has a file: keep up to the first ancestor with a DIFFERENT file.
* Checks both client filePath and serverComponents for file info.
* The ancestry array is bottom-up: index 0 = clicked element, last = root.

@@ -177,7 +192,7 @@ */

const clickedFile = items[0]?.filePath;
const clickedFile = getItemFilePath(items[0]!);
if (!clickedFile) {
// Clicked element has no file: find first ancestor with any file
const firstWithFile = items.findIndex((item) => item.filePath);
const firstWithFile = items.findIndex((item) => getItemFilePath(item));
if (firstWithFile === -1) return items;

@@ -189,3 +204,4 @@ return items.slice(0, firstWithFile + 1);

for (let i = 1; i < items.length; i++) {
if (items[i]!.filePath && items[i]!.filePath !== clickedFile) {
const ancestorFile = getItemFilePath(items[i]!);
if (ancestorFile && ancestorFile !== clickedFile) {
return items.slice(0, i + 1);

@@ -192,0 +208,0 @@ }