@treelocator/runtime 0.1.7 → 0.2.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/.turbo/turbo-build.log +15 -13
- package/.turbo/turbo-dev.log +32 -0
- package/.turbo/turbo-test.log +54 -10
- package/dist/adapters/adapterApi.d.ts +1 -1
- package/dist/adapters/createTreeNode.js +32 -4
- package/dist/adapters/jsx/getExpressionData.d.ts +1 -1
- package/dist/adapters/jsx/getExpressionData.js +8 -4
- package/dist/adapters/jsx/getJSXComponentBoundingBox.d.ts +1 -1
- package/dist/adapters/jsx/getJSXComponentBoundingBox.js +11 -6
- package/dist/adapters/jsx/jsxAdapter.js +13 -8
- package/dist/adapters/nextjs/parseNextjsDataAttributes.d.ts +31 -0
- package/dist/adapters/nextjs/parseNextjsDataAttributes.js +106 -0
- package/dist/adapters/phoenix/__tests__/parsePhoenixComments.test.d.ts +4 -0
- package/dist/adapters/phoenix/__tests__/parsePhoenixComments.test.js +218 -0
- package/dist/adapters/phoenix/detectPhoenix.d.ts +11 -0
- package/dist/adapters/phoenix/detectPhoenix.js +38 -0
- package/dist/adapters/phoenix/index.d.ts +10 -0
- package/dist/adapters/phoenix/index.js +9 -0
- package/dist/adapters/phoenix/parsePhoenixComments.d.ts +35 -0
- package/dist/adapters/phoenix/parsePhoenixComments.js +131 -0
- package/dist/adapters/phoenix/types.d.ts +16 -0
- package/dist/adapters/phoenix/types.js +1 -0
- package/dist/adapters/react/getFiberLabel.js +2 -1
- package/dist/components/MaybeOutline.js +65 -3
- package/dist/components/Runtime.js +1 -1
- package/dist/functions/formatAncestryChain.d.ts +3 -0
- package/dist/functions/formatAncestryChain.js +104 -15
- package/dist/functions/formatAncestryChain.test.js +26 -20
- package/dist/functions/normalizeFilePath.d.ts +14 -0
- package/dist/functions/normalizeFilePath.js +40 -0
- package/dist/output.css +87 -15
- package/dist/types/ServerComponentInfo.d.ts +14 -0
- package/dist/types/ServerComponentInfo.js +1 -0
- package/package.json +4 -3
- package/src/adapters/adapterApi.ts +1 -1
- package/src/adapters/createTreeNode.ts +35 -3
- package/src/adapters/jsx/getExpressionData.ts +9 -5
- package/src/adapters/jsx/getJSXComponentBoundingBox.ts +13 -8
- package/src/adapters/jsx/jsxAdapter.ts +14 -12
- package/src/adapters/nextjs/parseNextjsDataAttributes.ts +112 -0
- package/src/adapters/phoenix/__tests__/parsePhoenixComments.test.ts +264 -0
- package/src/adapters/phoenix/detectPhoenix.ts +44 -0
- package/src/adapters/phoenix/index.ts +11 -0
- package/src/adapters/phoenix/parsePhoenixComments.ts +140 -0
- package/src/adapters/phoenix/types.ts +16 -0
- package/src/adapters/react/getFiberLabel.ts +2 -1
- package/src/components/MaybeOutline.tsx +63 -4
- package/src/components/Runtime.tsx +3 -1
- package/src/functions/formatAncestryChain.test.ts +26 -20
- package/src/functions/formatAncestryChain.ts +121 -15
- package/src/functions/normalizeFilePath.ts +41 -0
- package/src/types/ServerComponentInfo.ts +14 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
|
|
2
|
-
> @treelocator/runtime@0.1.
|
|
2
|
+
> @treelocator/runtime@0.1.8 build /Users/wende/projects/locatorjs/packages/runtime
|
|
3
3
|
> concurrently pnpm:build:*
|
|
4
4
|
|
|
5
|
+
[tailwind]
|
|
6
|
+
[tailwind] > @treelocator/runtime@0.1.8 build:tailwind /Users/wende/projects/locatorjs/packages/runtime
|
|
7
|
+
[tailwind] > tailwindcss -i ./src/main.css -o ./dist/output.css
|
|
8
|
+
[tailwind]
|
|
5
9
|
[babel]
|
|
6
|
-
[babel] > @treelocator/runtime@0.1.
|
|
10
|
+
[babel] > @treelocator/runtime@0.1.8 build:babel /Users/wende/projects/locatorjs/packages/runtime
|
|
7
11
|
[babel] > babel src --out-dir dist --extensions .js,.jsx,.ts,.tsx
|
|
8
12
|
[babel]
|
|
9
|
-
[ts]
|
|
10
|
-
[ts] > @treelocator/runtime@0.1.7 build:ts /Users/wende/projects/locatorjs/packages/runtime
|
|
11
|
-
[ts] > tsc --declaration --emitDeclarationOnly --noEmit false --outDir dist
|
|
12
|
-
[ts]
|
|
13
13
|
[wrapImage]
|
|
14
|
-
[wrapImage] > @treelocator/runtime@0.1.
|
|
14
|
+
[wrapImage] > @treelocator/runtime@0.1.8 build:wrapImage /Users/wende/projects/locatorjs/packages/runtime
|
|
15
15
|
[wrapImage] > node ./scripts/wrapImage.js
|
|
16
16
|
[wrapImage]
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
20
|
-
[
|
|
17
|
+
[ts]
|
|
18
|
+
[ts] > @treelocator/runtime@0.1.8 build:ts /Users/wende/projects/locatorjs/packages/runtime
|
|
19
|
+
[ts] > tsc --declaration --emitDeclarationOnly --noEmit false --outDir dist
|
|
20
|
+
[ts]
|
|
21
21
|
[wrapImage] Tree icon file generated
|
|
22
22
|
[wrapImage] pnpm run build:wrapImage exited with code 0
|
|
23
|
+
[tailwind] [baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
|
23
24
|
[tailwind]
|
|
24
25
|
[tailwind] Rebuilding...
|
|
25
26
|
[tailwind]
|
|
26
|
-
[tailwind] Done in
|
|
27
|
+
[tailwind] Done in 173ms.
|
|
27
28
|
[tailwind] pnpm run build:tailwind exited with code 0
|
|
28
|
-
[babel]
|
|
29
|
+
[babel] [baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
|
30
|
+
[babel] Successfully compiled 80 files with Babel (499ms).
|
|
29
31
|
[babel] pnpm run build:babel exited with code 0
|
|
30
32
|
[ts] pnpm run build:ts exited with code 0
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
> @treelocator/runtime@0.1.8 dev /Users/wende/projects/locatorjs/packages/runtime
|
|
3
|
+
> concurrently pnpm:dev:*
|
|
4
|
+
|
|
5
|
+
[babel]
|
|
6
|
+
[babel] > @treelocator/runtime@0.1.8 dev:babel /Users/wende/projects/locatorjs/packages/runtime
|
|
7
|
+
[babel] > babel src --watch --out-dir dist --extensions .js,.jsx,.ts,.tsx
|
|
8
|
+
[babel]
|
|
9
|
+
[wrapImage]
|
|
10
|
+
[wrapImage] > @treelocator/runtime@0.1.8 dev:wrapImage /Users/wende/projects/locatorjs/packages/runtime
|
|
11
|
+
[wrapImage] > WATCH=true node ./scripts/wrapImage.js
|
|
12
|
+
[wrapImage]
|
|
13
|
+
[ts]
|
|
14
|
+
[ts] > @treelocator/runtime@0.1.8 dev:ts /Users/wende/projects/locatorjs/packages/runtime
|
|
15
|
+
[ts] > tsc --watch --declaration --emitDeclarationOnly --noEmit false --outDir dist --preserveWatchOutput
|
|
16
|
+
[ts]
|
|
17
|
+
[wrapCSS]
|
|
18
|
+
[wrapCSS] > @treelocator/runtime@0.1.8 dev:wrapCSS /Users/wende/projects/locatorjs/packages/runtime
|
|
19
|
+
[wrapCSS] > WATCH=true node ./scripts/wrapCSS.js
|
|
20
|
+
[wrapCSS]
|
|
21
|
+
[tailwind]
|
|
22
|
+
[tailwind] > @treelocator/runtime@0.1.8 dev:tailwind /Users/wende/projects/locatorjs/packages/runtime
|
|
23
|
+
[tailwind] > tailwindcss -i ./src/main.css -o ./dist/output.css --watch
|
|
24
|
+
[tailwind]
|
|
25
|
+
[tailwind] ELIFECYCLE Command failed.
|
|
26
|
+
[ts] ELIFECYCLE Command failed.
|
|
27
|
+
[wrapImage] ELIFECYCLE Command failed.
|
|
28
|
+
[wrapCSS] ELIFECYCLE Command failed.
|
|
29
|
+
[babel] ELIFECYCLE Command failed.
|
|
30
|
+
[babel] pnpm run dev:babel exited with code SIGINT
|
|
31
|
+
[wrapCSS] pnpm run dev:wrapCSS exited with code SIGINT
|
|
32
|
+
[wrapImage] pnpm run dev:wrapImage exited with code SIGINT
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,19 +1,63 @@
|
|
|
1
1
|
|
|
2
|
-
> @treelocator/runtime@0.1.
|
|
2
|
+
> @treelocator/runtime@0.1.8 test /Users/wende/projects/locatorjs/packages/runtime
|
|
3
3
|
> vitest run
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
RUN v2.1.9 /Users/wende/projects/locatorjs/packages/runtime
|
|
7
7
|
|
|
8
|
+
✓ src/functions/evalTemplate.test.ts (1 test) 1ms
|
|
9
|
+
✓ src/functions/transformPath.test.ts (3 tests) 1ms
|
|
8
10
|
✓ src/functions/mergeRects.test.ts (1 test) 2ms
|
|
9
|
-
✓ src/functions/
|
|
10
|
-
✓ src/functions/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
✓ src/functions/cropPath.test.ts (2 tests) 1ms
|
|
12
|
+
✓ src/functions/getUsableFileName.test.tsx (4 tests) 4ms
|
|
13
|
+
❯ src/functions/formatAncestryChain.test.ts (9 tests | 2 failed) 7ms
|
|
14
|
+
× formatAncestryChain > shows all owner components when ownerComponents is provided 4ms
|
|
15
|
+
→ expected 'div in App at src/App.jsx:104\n └─…' to be 'div in App at src/App.jsx:104\n └─…' // Object.is equality
|
|
16
|
+
× formatAncestryChain > shows single owner component without arrow when only one in chain 1ms
|
|
17
|
+
→ expected 'Button at src/Button.tsx:10' to be 'button in Button at src/Button.tsx:10' // Object.is equality
|
|
18
|
+
✓ src/adapters/phoenix/__tests__/parsePhoenixComments.test.ts (14 tests) 16ms
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
Tests 18 passed (18)
|
|
17
|
-
Start at 17:17:22
|
|
18
|
-
Duration 718ms (transform 282ms, setup 0ms, collect 336ms, tests 14ms, environment 1ms, prepare 463ms)
|
|
20
|
+
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
|
|
19
21
|
|
|
22
|
+
FAIL src/functions/formatAncestryChain.test.ts > formatAncestryChain > shows all owner components when ownerComponents is provided
|
|
23
|
+
AssertionError: expected 'div in App at src/App.jsx:104\n └─…' to be 'div in App at src/App.jsx:104\n └─…' // Object.is equality
|
|
24
|
+
|
|
25
|
+
- Expected
|
|
26
|
+
+ Received
|
|
27
|
+
|
|
28
|
+
div in App at src/App.jsx:104
|
|
29
|
+
- └─ div#sidebar-panel in Sidebar > GlassPanel at src/components/game/Sidebar.jsx:78
|
|
30
|
+
+ └─ GlassPanel#sidebar-panel in Sidebar at src/components/game/Sidebar.jsx:78
|
|
31
|
+
|
|
32
|
+
❯ src/functions/formatAncestryChain.test.ts:120:20
|
|
33
|
+
118|
|
|
34
|
+
119| const result = formatAncestryChain(items);
|
|
35
|
+
120| expect(result).toBe(
|
|
36
|
+
| ^
|
|
37
|
+
121| `div in App at src/App.jsx:104
|
|
38
|
+
122| └─ div#sidebar-panel in Sidebar > GlassPanel at src/components/gam…
|
|
39
|
+
|
|
40
|
+
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
|
|
41
|
+
|
|
42
|
+
FAIL src/functions/formatAncestryChain.test.ts > formatAncestryChain > shows single owner component without arrow when only one in chain
|
|
43
|
+
AssertionError: expected 'Button at src/Button.tsx:10' to be 'button in Button at src/Button.tsx:10' // Object.is equality
|
|
44
|
+
|
|
45
|
+
Expected: "button in Button at src/Button.tsx:10"
|
|
46
|
+
Received: "Button at src/Button.tsx:10"
|
|
47
|
+
|
|
48
|
+
❯ src/functions/formatAncestryChain.test.ts:138:20
|
|
49
|
+
136|
|
|
50
|
+
137| const result = formatAncestryChain(items);
|
|
51
|
+
138| expect(result).toBe("button in Button at src/Button.tsx:10");
|
|
52
|
+
| ^
|
|
53
|
+
139| });
|
|
54
|
+
140| });
|
|
55
|
+
|
|
56
|
+
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
|
|
57
|
+
|
|
58
|
+
Test Files 1 failed | 6 passed (7)
|
|
59
|
+
Tests 2 failed | 32 passed (34)
|
|
60
|
+
Start at 11:30:22
|
|
61
|
+
Duration 904ms (transform 370ms, setup 0ms, collect 434ms, tests 32ms, environment 497ms, prepare 595ms)
|
|
62
|
+
|
|
63
|
+
ELIFECYCLE Test failed. See above for more details.
|
|
@@ -8,7 +8,7 @@ export type ElementInfo = {
|
|
|
8
8
|
};
|
|
9
9
|
export type FullElementInfo = {
|
|
10
10
|
thisElement: ElementInfo;
|
|
11
|
-
htmlElement: HTMLElement;
|
|
11
|
+
htmlElement: HTMLElement | SVGElement;
|
|
12
12
|
parentElements: ElementInfo[];
|
|
13
13
|
componentBox: SimpleDOMRect;
|
|
14
14
|
componentsLabels: LabelData[];
|
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
import { ReactTreeNodeElement } from "./react/reactAdapter";
|
|
2
2
|
import { JSXTreeNodeElement } from "./jsx/jsxAdapter";
|
|
3
|
-
import {
|
|
3
|
+
import { SvelteTreeNodeElement } from "./svelte/svelteAdapter";
|
|
4
|
+
import { VueTreeNodeElement } from "./vue/vueAdapter";
|
|
5
|
+
import { detectJSX, detectReact, detectSvelte, detectVue } from "@locator/shared";
|
|
6
|
+
import { detectPhoenix } from "./phoenix/detectPhoenix";
|
|
4
7
|
export function createTreeNode(element, adapterId) {
|
|
5
|
-
// Check for
|
|
6
|
-
if (adapterId === "react"
|
|
8
|
+
// Check for explicit adapter ID first
|
|
9
|
+
if (adapterId === "react") {
|
|
10
|
+
return new ReactTreeNodeElement(element);
|
|
11
|
+
}
|
|
12
|
+
if (adapterId === "svelte") {
|
|
13
|
+
return new SvelteTreeNodeElement(element);
|
|
14
|
+
}
|
|
15
|
+
if (adapterId === "vue") {
|
|
16
|
+
return new VueTreeNodeElement(element);
|
|
17
|
+
}
|
|
18
|
+
if (adapterId === "jsx") {
|
|
19
|
+
return new JSXTreeNodeElement(element);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Auto-detect framework
|
|
23
|
+
if (detectSvelte()) {
|
|
24
|
+
return new SvelteTreeNodeElement(element);
|
|
25
|
+
}
|
|
26
|
+
if (detectVue()) {
|
|
27
|
+
return new VueTreeNodeElement(element);
|
|
28
|
+
}
|
|
29
|
+
if (detectReact()) {
|
|
7
30
|
return new ReactTreeNodeElement(element);
|
|
8
31
|
}
|
|
9
32
|
|
|
10
33
|
// Check for JSX adapter (babel plugin) - check if element has data-locatorjs-id
|
|
11
|
-
if (
|
|
34
|
+
if (detectJSX() || element.dataset.locatorjsId) {
|
|
35
|
+
return new JSXTreeNodeElement(element);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check for Phoenix LiveView (uses JSX adapter as fallback for pure Phoenix apps)
|
|
39
|
+
if (detectPhoenix()) {
|
|
12
40
|
return new JSXTreeNodeElement(element);
|
|
13
41
|
}
|
|
14
42
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ExpressionInfo, FileStorage } from "@locator/shared";
|
|
2
|
-
export declare function getExpressionData(target:
|
|
2
|
+
export declare function getExpressionData(target: Element, fileData: FileStorage | null): ExpressionInfo | null;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { parseDataId, parseDataPath } from "../../functions/parseDataId";
|
|
2
2
|
export function getExpressionData(target, fileData) {
|
|
3
|
+
// Use getAttribute instead of dataset to support both HTML and SVG elements
|
|
4
|
+
const dataLocatorjs = target.getAttribute("data-locatorjs");
|
|
5
|
+
const dataLocatorjsId = target.getAttribute("data-locatorjs-id");
|
|
6
|
+
|
|
3
7
|
// First check for data-locatorjs (path-based, for server components)
|
|
4
|
-
if (
|
|
5
|
-
const parsed = parseDataPath(
|
|
8
|
+
if (dataLocatorjs) {
|
|
9
|
+
const parsed = parseDataPath(dataLocatorjs);
|
|
6
10
|
if (parsed) {
|
|
7
11
|
const [, line, column] = parsed;
|
|
8
12
|
|
|
@@ -33,8 +37,8 @@ export function getExpressionData(target, fileData) {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
// Fall back to data-locatorjs-id (ID-based, traditional approach)
|
|
36
|
-
if (
|
|
37
|
-
const [, id] = parseDataId(
|
|
40
|
+
if (dataLocatorjsId && fileData) {
|
|
41
|
+
const [, id] = parseDataId(dataLocatorjsId);
|
|
38
42
|
const expData = fileData.expressions[Number(id)];
|
|
39
43
|
if (expData) {
|
|
40
44
|
return expData;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FileStorage } from "@locator/shared";
|
|
2
2
|
import type { SimpleDOMRect } from "../../types/types";
|
|
3
|
-
export declare function getJSXComponentBoundingBox(found:
|
|
3
|
+
export declare function getJSXComponentBoundingBox(found: Element, locatorData: {
|
|
4
4
|
[filename: string]: FileStorage;
|
|
5
5
|
}, componentFolder: string, componentId: number): SimpleDOMRect;
|
|
@@ -9,19 +9,24 @@ export function getJSXComponentBoundingBox(found, locatorData, componentFolder,
|
|
|
9
9
|
if (!parent) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
// Support both HTMLElement and SVGElement
|
|
13
|
+
if (parent instanceof HTMLElement || parent instanceof SVGElement) {
|
|
14
|
+
// Use getAttribute instead of dataset to support both HTML and SVG elements
|
|
15
|
+
const dataLocatorjs = parent.getAttribute("data-locatorjs");
|
|
16
|
+
const dataLocatorjsId = parent.getAttribute("data-locatorjs-id");
|
|
17
|
+
|
|
13
18
|
// Check for either data-locatorjs (path-based) or data-locatorjs-id (ID-based)
|
|
14
|
-
if (
|
|
19
|
+
if (dataLocatorjs || dataLocatorjsId) {
|
|
15
20
|
let fileFullPath;
|
|
16
|
-
if (
|
|
17
|
-
const parsed = parseDataPath(
|
|
21
|
+
if (dataLocatorjs) {
|
|
22
|
+
const parsed = parseDataPath(dataLocatorjs);
|
|
18
23
|
if (!parsed) {
|
|
19
24
|
goParent(parent);
|
|
20
25
|
return;
|
|
21
26
|
}
|
|
22
27
|
[fileFullPath] = parsed;
|
|
23
|
-
} else if (
|
|
24
|
-
[fileFullPath] = parseDataId(
|
|
28
|
+
} else if (dataLocatorjsId) {
|
|
29
|
+
[fileFullPath] = parseDataId(dataLocatorjsId);
|
|
25
30
|
} else {
|
|
26
31
|
goParent(parent);
|
|
27
32
|
return;
|
|
@@ -5,10 +5,13 @@ import { getExpressionData } from "./getExpressionData";
|
|
|
5
5
|
import { getJSXComponentBoundingBox } from "./getJSXComponentBoundingBox";
|
|
6
6
|
export function getElementInfo(target) {
|
|
7
7
|
const found = target.closest("[data-locatorjs-id], [data-locatorjs]");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
|
|
9
|
+
// Support both HTMLElement and SVGElement
|
|
10
|
+
// SVG elements don't have dataset, so use getAttribute instead
|
|
11
|
+
const dataId = found?.getAttribute("data-locatorjs-id");
|
|
12
|
+
const dataPath = found?.getAttribute("data-locatorjs");
|
|
13
|
+
const styledDataId = found?.getAttribute("data-locatorjs-styled");
|
|
14
|
+
if (found && (found instanceof HTMLElement || found instanceof SVGElement) && (dataId || dataPath || styledDataId)) {
|
|
12
15
|
if (!dataId && !dataPath) {
|
|
13
16
|
return null;
|
|
14
17
|
}
|
|
@@ -89,8 +92,9 @@ export function getElementInfo(target) {
|
|
|
89
92
|
}
|
|
90
93
|
export class JSXTreeNodeElement extends HtmlElementTreeNode {
|
|
91
94
|
getSource() {
|
|
92
|
-
|
|
93
|
-
const
|
|
95
|
+
// Use getAttribute instead of dataset to support both HTML and SVG elements
|
|
96
|
+
const dataId = this.element.getAttribute("data-locatorjs-id");
|
|
97
|
+
const dataPath = this.element.getAttribute("data-locatorjs");
|
|
94
98
|
if (!dataId && !dataPath) {
|
|
95
99
|
return null;
|
|
96
100
|
}
|
|
@@ -131,8 +135,9 @@ export class JSXTreeNodeElement extends HtmlElementTreeNode {
|
|
|
131
135
|
return null;
|
|
132
136
|
}
|
|
133
137
|
getComponent() {
|
|
134
|
-
|
|
135
|
-
const
|
|
138
|
+
// Use getAttribute instead of dataset to support both HTML and SVG elements
|
|
139
|
+
const dataId = this.element.getAttribute("data-locatorjs-id");
|
|
140
|
+
const dataPath = this.element.getAttribute("data-locatorjs");
|
|
136
141
|
if (!dataId && !dataPath) {
|
|
137
142
|
return null;
|
|
138
143
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ServerComponentInfo } from "../../types/ServerComponentInfo";
|
|
2
|
+
/**
|
|
3
|
+
* Get the data-locatorjs attribute from the current element only.
|
|
4
|
+
* Returns array with single component info, or empty array if not found.
|
|
5
|
+
*
|
|
6
|
+
* We only look at the current element because the tree structure already
|
|
7
|
+
* shows the hierarchy - each parent element will have its own server component.
|
|
8
|
+
*
|
|
9
|
+
* Example DOM:
|
|
10
|
+
* ```html
|
|
11
|
+
* <html data-locatorjs="/app/layout.tsx:27:4"> <!-- RootLayout -->
|
|
12
|
+
* <body data-locatorjs="/app/layout.tsx:28:6"> <!-- RootLayout -->
|
|
13
|
+
* <div data-locatorjs="/app/page.tsx:5:4"> <!-- Page -->
|
|
14
|
+
* <button>Click</button>
|
|
15
|
+
* </div>
|
|
16
|
+
* </body>
|
|
17
|
+
* </html>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* For the div, returns: [{ name: "Page", filePath: "/app/page.tsx", line: 5 }]
|
|
21
|
+
* The tree structure will show: html (RootLayout) > body (RootLayout) > div (Page)
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectNextjsServerComponents(element: Element): ServerComponentInfo[];
|
|
24
|
+
/**
|
|
25
|
+
* Main entry point: extract Next.js server component info from element.
|
|
26
|
+
* Returns null if no data-locatorjs attribute found.
|
|
27
|
+
*
|
|
28
|
+
* This function is called during ancestry collection to enrich each AncestryItem
|
|
29
|
+
* with server-side Next.js component information.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseNextjsServerComponents(element: Element): ServerComponentInfo[] | null;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { normalizeFilePath } from "../../functions/normalizeFilePath";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse Next.js server component data from data-locatorjs attribute.
|
|
5
|
+
*
|
|
6
|
+
* Format: data-locatorjs="/path/to/app/layout.tsx:27:4"
|
|
7
|
+
*
|
|
8
|
+
* The @treelocator/webpack-loader adds these attributes to elements
|
|
9
|
+
* rendered by Next.js Server Components.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extract component name from file path.
|
|
14
|
+
* Examples:
|
|
15
|
+
* - "/apps/next-16/app/layout.tsx:27:4" → "RootLayout"
|
|
16
|
+
* - "/apps/next-16/app/page.tsx:5:4" → "Home"
|
|
17
|
+
* - "/apps/next-16/app/components/Header.tsx:10:2" → "Header"
|
|
18
|
+
*/
|
|
19
|
+
function extractComponentName(filePath) {
|
|
20
|
+
// Remove line:column suffix
|
|
21
|
+
const pathOnly = filePath.split(":")[0] || filePath;
|
|
22
|
+
|
|
23
|
+
// Get filename without extension
|
|
24
|
+
const fileName = pathOnly.split("/").pop()?.replace(/\.(tsx?|jsx?)$/, "") || "Unknown";
|
|
25
|
+
|
|
26
|
+
// Common Next.js conventions:
|
|
27
|
+
// - "layout" → "RootLayout" or "Layout"
|
|
28
|
+
// - "page" → Component name (we don't know it, so use "Page")
|
|
29
|
+
// - Others → Use as-is
|
|
30
|
+
if (fileName === "layout") {
|
|
31
|
+
return "RootLayout";
|
|
32
|
+
} else if (fileName === "page") {
|
|
33
|
+
return "Page";
|
|
34
|
+
}
|
|
35
|
+
return fileName;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse a data-locatorjs attribute value.
|
|
40
|
+
* Format: "/path/to/file.tsx:line:column"
|
|
41
|
+
* Returns ServerComponentInfo or null if parsing fails.
|
|
42
|
+
*/
|
|
43
|
+
function parseDataLocatorjsValue(value) {
|
|
44
|
+
if (!value) return null;
|
|
45
|
+
|
|
46
|
+
// Split by ":" to get [filePath, line, column]
|
|
47
|
+
const parts = value.split(":");
|
|
48
|
+
if (parts.length < 2) return null;
|
|
49
|
+
|
|
50
|
+
// Last two parts are column and line (in reverse order)
|
|
51
|
+
const column = parts.pop();
|
|
52
|
+
const line = parts.pop();
|
|
53
|
+
|
|
54
|
+
// Everything else is the file path (which may contain colons on Windows)
|
|
55
|
+
const filePath = parts.join(":");
|
|
56
|
+
if (!filePath || !line) return null;
|
|
57
|
+
const componentName = extractComponentName(filePath);
|
|
58
|
+
const normalizedPath = normalizeFilePath(filePath);
|
|
59
|
+
return {
|
|
60
|
+
name: componentName,
|
|
61
|
+
filePath: normalizedPath,
|
|
62
|
+
line: parseInt(line, 10),
|
|
63
|
+
type: "component"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the data-locatorjs attribute from the current element only.
|
|
69
|
+
* Returns array with single component info, or empty array if not found.
|
|
70
|
+
*
|
|
71
|
+
* We only look at the current element because the tree structure already
|
|
72
|
+
* shows the hierarchy - each parent element will have its own server component.
|
|
73
|
+
*
|
|
74
|
+
* Example DOM:
|
|
75
|
+
* ```html
|
|
76
|
+
* <html data-locatorjs="/app/layout.tsx:27:4"> <!-- RootLayout -->
|
|
77
|
+
* <body data-locatorjs="/app/layout.tsx:28:6"> <!-- RootLayout -->
|
|
78
|
+
* <div data-locatorjs="/app/page.tsx:5:4"> <!-- Page -->
|
|
79
|
+
* <button>Click</button>
|
|
80
|
+
* </div>
|
|
81
|
+
* </body>
|
|
82
|
+
* </html>
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* For the div, returns: [{ name: "Page", filePath: "/app/page.tsx", line: 5 }]
|
|
86
|
+
* The tree structure will show: html (RootLayout) > body (RootLayout) > div (Page)
|
|
87
|
+
*/
|
|
88
|
+
export function collectNextjsServerComponents(element) {
|
|
89
|
+
const value = element.getAttribute("data-locatorjs");
|
|
90
|
+
if (!value) return [];
|
|
91
|
+
const info = parseDataLocatorjsValue(value);
|
|
92
|
+
return info ? [info] : [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Main entry point: extract Next.js server component info from element.
|
|
97
|
+
* Returns null if no data-locatorjs attribute found.
|
|
98
|
+
*
|
|
99
|
+
* This function is called during ancestry collection to enrich each AncestryItem
|
|
100
|
+
* with server-side Next.js component information.
|
|
101
|
+
*/
|
|
102
|
+
export function parseNextjsServerComponents(element) {
|
|
103
|
+
const components = collectNextjsServerComponents(element);
|
|
104
|
+
if (components.length === 0) return null;
|
|
105
|
+
return components;
|
|
106
|
+
}
|