Skip to content

Commit 1d54b8a

Browse files
author
blucas.wu
committed
(v1.3.0): support locate source code
1 parent a6ca87c commit 1d54b8a

File tree

32 files changed

+1019
-313
lines changed

32 files changed

+1019
-313
lines changed

Diff for: .gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ yarn-error.log*
1919
/dist
2020
/docs-dist
2121
test.ts
22-
public/page-spy/index.min.js
22+
public/*
23+
!public/favicon*
2324

2425
# misc
2526
.DS_Store

Diff for: LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2022-present Blucas
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Diff for: index.html

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88
<title>PageSpy</title>
99
<link rel="icon" href="/favicon.ico">
1010
<link rel="shortcut icon" type="image/svg+xml" href="/favicon.svg">
11+
<script src="/source-map/source-map.min.js"></script>
12+
<script>
13+
const mappingWasmUrl = new URL('/source-map/mappings.wasm', window.location.href).toString()
14+
sourceMap.SourceMapConsumer.initialize({
15+
"lib/mappings.wasm": mappingWasmUrl
16+
});
17+
</script>
18+
<script src="/shiki/dist/index.jsdelivr.iife.js"></script>
19+
<script>
20+
const shikiURL = new URL('/shiki', window.location.origin).toString()
21+
window.shiki.setCDN(shikiURL)
22+
</script>
1123
</head>
1224

1325
<body>

Diff for: package.json

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
{
22
"name": "@huolala-tech/page-spy-web",
3-
"version": "1.2.7",
3+
"version": "1.3.0",
44
"description": "All-In-One Remote Debugging Tool",
55
"homepage": "https://huolalatech.github.io/page-spy-web",
66
"license": "MIT",
77
"scripts": {
88
"start:doc": "vite --mode doc",
9-
"prestart:client": "node scripts/mv-sdk.js",
9+
"prestart:client": "bash scripts/public-files.sh",
1010
"start:client": "vite --mode client",
1111
"start:server": "page-spy-api",
1212
"build:doc": "vite build --mode doc",
13-
"prebuild:client": "node scripts/mv-sdk.js",
13+
"prebuild:client": "bash scripts/public-files.sh",
1414
"build:client": "vite build --mode client",
1515
"preview": "vite preview",
1616
"lint": "eslint --ext .js,.jsx,.ts,.tsx ./src"
1717
},
1818
"devDependencies": {
1919
"@babel/eslint-parser": "^7.19.1",
20-
"@huolala-tech/page-spy": "^1.2.5",
20+
"@huolala-tech/page-spy": "^1.3.0",
2121
"@mdx-js/rollup": "^2.3.0",
2222
"@types/lodash-es": "^4.17.7",
2323
"@types/mdx": "^2.0.4",
@@ -51,6 +51,7 @@
5151
"antd": "^5.2.1",
5252
"clsx": "^1.2.1",
5353
"copy-to-clipboard": "^3.3.3",
54+
"error-stack-parser": "^2.1.4",
5455
"highlight.js": "^11.7.0",
5556
"i18next": "^22.4.10",
5657
"i18next-browser-languagedetector": "^7.0.1",
@@ -64,6 +65,8 @@
6465
"react-transition-group": "^4.4.5",
6566
"rehype-parse": "^8.0.4",
6667
"rehype-stringify": "^9.0.3",
68+
"shiki": "^0.14.3",
69+
"source-map": "^0.7.4",
6770
"unified": "^10.1.2",
6871
"unist-util-visit": "^4.1.2",
6972
"zustand": "^4.3.7"

Diff for: scripts/mv-sdk.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,30 @@ const fs = require('fs');
33
const path = require('path');
44

55
const root = process.cwd();
6-
const targetDir = path.resolve(root, `public/page-spy`);
76

8-
try {
9-
fs.mkdirSync(targetDir, { recursive: true });
7+
const pageSpy = require.resolve('@huolala-tech/page-spy');
8+
const sourceMap = require.resolve('source-map');
9+
const fileList = [
10+
{
11+
from: pageSpy,
12+
to: path.resolve(root, 'public/page-spy/index.min.js'),
13+
},
14+
{
15+
from: path.resolve(path.dirname(sourceMap), './dist/source-map.js'),
16+
to: path.resolve(root, 'public/source-map/source-map.min.js'),
17+
},
18+
{
19+
from: path.resolve(path.dirname(sourceMap), './lib/mappings.wasm'),
20+
to: path.resolve(root, 'public/source-map/mappings.wasm'),
21+
},
22+
];
1023

11-
const src = require.resolve('@huolala-tech/page-spy');
12-
const dest = path.resolve(targetDir, 'index.min.js');
13-
fs.copyFileSync(src, dest);
24+
try {
25+
fileList.forEach(({ from, to }) => {
26+
fs.mkdirSync(path.dirname(to), { recursive: true });
27+
fs.copyFileSync(from, to);
28+
});
1429
} catch (e) {
15-
console.error('PageSpy sdk copy failed.');
1630
console.error(e);
1731
process.exit(1);
1832
}

Diff for: scripts/public-files.sh

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/bash
2+
3+
root=$(pwd)
4+
5+
# SDK
6+
target_sdk="${root}/public/page-spy"
7+
mkdir -p "$target_sdk"
8+
cp "${root}/node_modules/@huolala-tech/page-spy/dist/index.min.js" "${root}/public/page-spy/index.min.js"
9+
10+
# source-map
11+
target_sourcemap="${root}/public/source-map"
12+
mkdir -p "$target_sourcemap"
13+
cp "${root}/node_modules/source-map/dist/source-map.js" "${root}/public/source-map/source-map.min.js"
14+
cp "${root}/node_modules/source-map/lib/mappings.wasm" "${root}/public/source-map/mappings.wasm"
15+
16+
# shiki
17+
cp -R "${root}/node_modules/shiki" "${root}/public/shiki"

Diff for: src/assets/image/error-stack.svg

+1
Loading

Diff for: src/assets/locales/en.json

+15-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,21 @@
7373
"console": {
7474
"run": "Run",
7575
"placeholder": "Executable code",
76-
"newContent": "New content"
76+
"newContent": "New content",
77+
"error-trace": {
78+
"title": "Error detail",
79+
"hints": "HINTS",
80+
"message-title": "Error message",
81+
"stack-title": "Error stack",
82+
"source-filename": "Source filename",
83+
"fetch-minify-fail": "Fail to fetch origin file",
84+
"none-sourcemap": "No sourcemap file founded",
85+
"fetch-sourcemap-fail": "Fail to fetch sourcemap file",
86+
"failed-title": "There are some issues with locating the source code",
87+
"failed-advice": "Here are some suggestions to fix and improve the situation:",
88+
"fix-suggestion-1": "<0>1. Ensure file existence: When locating the source code for each error stack, two files need to be requested - the compressed file and the sourcemap file. The rule for obtaining the sourcemap file is based on the address indicated by</0> <1>//# sourceMappingURL=*</1> <2>in the contents of the compressed file;</2>",
89+
"fix-suggestion-2": "2. Troubleshoot network issues: If there are access restrictions on the server where your source files are located, it may lead to failed requests when trying to access the resources. You can open the devtool to check if the network is working properly."
90+
}
7791
},
7892
"network": {},
7993
"system": {

Diff for: src/assets/locales/zh.json

+15-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,21 @@
7373
"console": {
7474
"run": "执行",
7575
"placeholder": "可执行代码",
76-
"newContent": "新消息"
76+
"newContent": "新消息",
77+
"error-trace": {
78+
"title": "错误详情",
79+
"hints": "使用须知",
80+
"message-title": " 错误信息",
81+
"stack-title": "错误栈",
82+
"source-filename": "源文件",
83+
"fetch-minify-fail": "获取源文件失败",
84+
"none-sourcemap": "未找到 SourceMap",
85+
"fetch-sourcemap-fail": "获取 SourceMap 失败",
86+
"failed-title": "定位源码时出现一些故障",
87+
"failed-advice": "以下是修复和改进的建议:",
88+
"fix-suggestion-1": "<0>1. 确保文件存在:每个错误栈定位源码时都需要请求两个文件,分别是压缩文件和 sourcemap 文件,sourcemap 文件的获取规则是根据压缩文件内容中的</0><1>//# sourceMappingURL=*</1><2>所指向的地址;</2>",
89+
"fix-suggestion-2": "2. 排除网络故障:如果你的源文件所在服务器有限制访问规则,则可能导致请求资源时出现失败,你可以打开控制台查看网络请求是否正常。"
90+
}
7791
},
7892
"network": {},
7993
"system": {

Diff for: src/components/BlockTitle/index.less

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.block-title {
2+
position: relative;
3+
padding-left: 16px;
4+
&::before {
5+
content: ' ';
6+
display: block;
7+
position: absolute;
8+
left: 0;
9+
top: 50%;
10+
width: 4px;
11+
height: 60%;
12+
transform: translateY(-50%);
13+
background-color: @primary-color;
14+
}
15+
~ .block-title {
16+
margin-top: 32px;
17+
}
18+
}

Diff for: src/components/BlockTitle/index.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Typography } from 'antd';
2+
import type { TitleProps } from 'antd/lib/typography/Title';
3+
import './index.less';
4+
5+
const { Title } = Typography;
6+
7+
export const BlockTitle = ({
8+
title,
9+
level = 3,
10+
}: {
11+
title: React.ReactNode;
12+
level?: TitleProps['level'];
13+
}) => {
14+
return (
15+
<div className="block-title">
16+
<Title level={level} style={{ color: 'rgba(0, 0, 0, 0.65)' }}>
17+
{title}
18+
</Title>
19+
</div>
20+
);
21+
};

Diff for: src/main.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import ReactDOM from 'react-dom';
1+
import ReactDOM from 'react-dom/client';
22
import { App } from './App';
33
import '@/assets/style/union.less';
44
import '@/assets/style/initial.css';
55
import '@/assets/locales/index';
66

7-
ReactDOM.render(<App />, document.querySelector('#root'));
7+
const root = ReactDOM.createRoot(document.querySelector('#root')!);
8+
9+
root.render(<App />);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.error-trace {
2+
display: flex;
3+
flex-wrap: nowrap;
4+
align-items: flex-start;
5+
6+
&-node {
7+
flex: 1;
8+
padding-inline: 8px;
9+
}
10+
11+
&-icon {
12+
color: #bbb;
13+
font-size: 20px;
14+
cursor: pointer;
15+
&:hover {
16+
color: #999;
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { SpyConsole } from '@huolala-tech/page-spy';
2+
import { ReactComponent as ErrorStackSvg } from '@/assets/image/error-stack.svg';
3+
import './index.less';
4+
import Icon from '@ant-design/icons';
5+
import { useCallback } from 'react';
6+
import ErrorStackParser from 'error-stack-parser';
7+
8+
export type RequiredFrames = Required<StackFrame>[];
9+
10+
export const isErrorTraceNode = (
11+
item: SpyConsole.DataItem,
12+
): item is RRequired<SpyConsole.DataItem> => {
13+
if (
14+
item.logType === 'error' &&
15+
item.logs.length === 1 &&
16+
item.errorDetail &&
17+
item.errorDetail.stack
18+
) {
19+
return true;
20+
}
21+
return false;
22+
};
23+
24+
export const ErrorTraceNode = ({
25+
data,
26+
}: {
27+
data: RRequired<SpyConsole.DataItem>;
28+
}) => {
29+
const onPopupDetail = useCallback(() => {
30+
if (!data.errorDetail.stack) {
31+
return;
32+
}
33+
34+
const error = new Error();
35+
36+
const { name, message, stack } = data.errorDetail;
37+
error.name = name;
38+
error.message = message;
39+
error.stack = stack;
40+
const frames = ErrorStackParser.parse(error).filter(
41+
({ fileName, lineNumber, columnNumber }) => {
42+
return [fileName, lineNumber, columnNumber].every(Boolean);
43+
},
44+
) as RequiredFrames;
45+
if (!frames.length) return;
46+
47+
window.dispatchEvent(
48+
new CustomEvent('source-code-detail', {
49+
detail: {
50+
error,
51+
frames,
52+
},
53+
}),
54+
);
55+
}, [data.errorDetail]);
56+
57+
return (
58+
<div className="error-trace">
59+
<Icon
60+
component={ErrorStackSvg}
61+
className="error-trace-icon"
62+
onClick={onPopupDetail}
63+
/>
64+
<div className="error-trace-node">
65+
<code>{data.errorDetail.stack}</code>
66+
</div>
67+
</div>
68+
);
69+
};

Diff for: src/pages/Devtools/ConsolePanel/components/ConsoleNode/index.less

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
.console-node {
22
margin-right: 7px;
3-
font-size: 12px;
43
word-break: break-word;
54
&.origin {
65
color: #323941;

Diff for: src/pages/Devtools/ConsolePanel/components/ConsoleNode/index.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable no-underscore-dangle */
2-
import type { ReactNode } from 'react';
2+
import type { MouseEventHandler, ReactNode } from 'react';
33
import React, { useCallback, useEffect, useState } from 'react';
44
import { CaretRightOutlined } from '@ant-design/icons';
55
import './index.less';
@@ -34,7 +34,7 @@ function GetterNode({ id, parentId, instanceId, keyName }: GetterNodeProps) {
3434
};
3535
}, [id, keyName, socket]);
3636

37-
const getPropertyValue = useCallback(
37+
const getPropertyValue: MouseEventHandler = useCallback(
3838
(evt) => {
3939
if (!id) return;
4040
evt.stopPropagation();
@@ -152,7 +152,7 @@ const PropertyItem = React.memo<{
152152
const PrototypeKey = '[[Prototype]]';
153153
interface AtomNodeProps {
154154
id: string;
155-
value: string | PropertyDescriptor | ReactNode;
155+
value: string | ReactNode;
156156
showArrow?: boolean;
157157
}
158158
function AtomNode({ id, value, showArrow = true }: AtomNodeProps) {
@@ -236,7 +236,7 @@ interface ConsoleNodeProps {
236236
const ConsoleNode = React.memo<ConsoleNodeProps>(({ data }) => {
237237
const { __atomId = '', type, value } = data;
238238
if (type === 'atom' && !!__atomId) {
239-
return <AtomNode id={__atomId} value={value} />;
239+
return <AtomNode id={__atomId} value={value as string} />;
240240
}
241241
// new Boolean/String/Number...
242242
// e.g. new Boolean() => { type: 'object', value: false }

0 commit comments

Comments
 (0)