@forge-kit/plugin-source-map-prase 0.0.1 → 0.0.3
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/dist/app.js +3 -5
- package/dist/node.js +2556 -0
- package/package.json +5 -3
- package/src/App.less +43 -74
- package/src/App.tsx +98 -54
- package/src/components/map-input-panel/index.less +71 -79
- package/src/components/map-input-panel/index.tsx +65 -69
- package/src/components/panel-card/index.less +21 -0
- package/src/components/panel-card/index.tsx +6 -8
- package/src/components/trace-chain/index.less +126 -125
- package/src/components/trace-chain/index.tsx +25 -24
- package/src/main.tsx +1 -1
- package/src/node/index.ts +6 -0
- package/src/{utils/source-map → node/trace/core}/base/registry.ts +14 -11
- package/src/{utils/source-map → node/trace/core}/base/types.ts +1 -0
- package/src/node/trace/core/domain/chain-slots.ts +33 -0
- package/src/{utils/source-map → node/trace/core}/domain/source-content.ts +15 -2
- package/src/node/trace/core/domain/trace-resolver.ts +179 -0
- package/src/node/trace/core/domain/view-model.ts +57 -0
- package/src/node/trace/resolve/context.ts +59 -0
- package/src/node/trace/resolve/index.ts +97 -0
- package/src/node/trace/resolve/input.ts +35 -0
- package/src/node/trace/resolve/snippet-limit.ts +35 -0
- package/src/node/trace/resolve/types.ts +37 -0
- package/src/node/trace/runner.ts +149 -0
- package/src/shared/trace-common.ts +104 -0
- package/src/shared/trace-contract.ts +29 -0
- package/src/types.ts +19 -0
- package/src/utils/trace-ui/index.ts +12 -0
- package/src/utils/trace-ui/state.ts +81 -0
- package/src/utils/source-map/domain/chain-slots.ts +0 -59
- package/src/utils/source-map/domain/trace-resolver.ts +0 -165
- package/src/utils/source-map/domain/view-model.ts +0 -20
- package/src/utils/source-map/facade/source-map-utils.ts +0 -212
- package/src/utils/source-map/index.ts +0 -18
- /package/src/{utils/source-map → node/trace/core}/base/constants.ts +0 -0
- /package/src/{utils/source-map → node/trace/core}/base/path.ts +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export const DEFAULT_CONTEXT_LINE_RADIUS = 3
|
|
2
|
+
|
|
3
|
+
export interface TraceLocation {
|
|
4
|
+
fileName: string
|
|
5
|
+
lineNumber: number
|
|
6
|
+
columnNumber: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ResolvedSourceMeta {
|
|
10
|
+
sourceFile: string
|
|
11
|
+
lineNumber: number
|
|
12
|
+
columnNumber: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TraceSnippetState {
|
|
16
|
+
traceCode: string
|
|
17
|
+
resolvedSourceMeta: ResolvedSourceMeta | null
|
|
18
|
+
traceHighlightLines: number[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TraceMetaOutputState<TTraceHop = unknown> {
|
|
22
|
+
traceData: TTraceHop[]
|
|
23
|
+
resolvedSourceMeta: ResolvedSourceMeta | null
|
|
24
|
+
canFetchSourceSnippet: boolean
|
|
25
|
+
message: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ResolveInputConfig {
|
|
29
|
+
entryFileName: string
|
|
30
|
+
entryLine: string
|
|
31
|
+
entryColumn: string
|
|
32
|
+
contextLineRadius: string
|
|
33
|
+
mapCount: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ResolveInputValidation =
|
|
37
|
+
| { ok: true; entry: TraceLocation; contextLineRadius: number }
|
|
38
|
+
| { ok: false; message: string }
|
|
39
|
+
|
|
40
|
+
export interface TraceOutputState<TTraceHop = unknown> {
|
|
41
|
+
traceData: TTraceHop[]
|
|
42
|
+
traceCode: string
|
|
43
|
+
resolvedSourceMeta: ResolvedSourceMeta | null
|
|
44
|
+
traceHighlightLines: number[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const createInitialOutputState = <TTraceHop = unknown>(): TraceOutputState<TTraceHop> => {
|
|
48
|
+
return {
|
|
49
|
+
traceData: [],
|
|
50
|
+
traceCode: "请先上传一个或多个 .map 文件。",
|
|
51
|
+
resolvedSourceMeta: null,
|
|
52
|
+
traceHighlightLines: [],
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const patchOutputMessage = <TTraceHop = unknown>(
|
|
57
|
+
prev: TraceOutputState<TTraceHop>,
|
|
58
|
+
message: string,
|
|
59
|
+
): TraceOutputState<TTraceHop> => {
|
|
60
|
+
return {
|
|
61
|
+
...prev,
|
|
62
|
+
traceCode: message,
|
|
63
|
+
traceHighlightLines: [],
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const validateResolveInput = (config: ResolveInputConfig): ResolveInputValidation => {
|
|
68
|
+
const parsedLine = Number(config.entryLine)
|
|
69
|
+
const parsedColumn = Number(config.entryColumn)
|
|
70
|
+
const parsedContextLineRadius = Number(config.contextLineRadius)
|
|
71
|
+
const normalizedFileName = config.entryFileName.replace(/\\/g, "/").trim()
|
|
72
|
+
|
|
73
|
+
if (!normalizedFileName) {
|
|
74
|
+
return {ok: false, message: "请输入入口文件名,例如 index.js"}
|
|
75
|
+
}
|
|
76
|
+
if (!config.entryLine.trim()) {
|
|
77
|
+
return {ok: false, message: "请输入入口行号(1-based)。"}
|
|
78
|
+
}
|
|
79
|
+
if (!config.entryColumn.trim()) {
|
|
80
|
+
return {ok: false, message: "请输入入口列号(0-based)。"}
|
|
81
|
+
}
|
|
82
|
+
if (!Number.isInteger(parsedLine) || parsedLine < 1) {
|
|
83
|
+
return {ok: false, message: "行号必须是大于等于 1 的整数。"}
|
|
84
|
+
}
|
|
85
|
+
if (!Number.isInteger(parsedColumn) || parsedColumn < 0) {
|
|
86
|
+
return {ok: false, message: "列号必须是大于等于 0 的整数。"}
|
|
87
|
+
}
|
|
88
|
+
if (!Number.isInteger(parsedContextLineRadius) || parsedContextLineRadius < 0) {
|
|
89
|
+
return {ok: false, message: "上下文行数必须是大于等于 0 的整数。"}
|
|
90
|
+
}
|
|
91
|
+
if (config.mapCount === 0) {
|
|
92
|
+
return {ok: false, message: "未加载可用 map 文件,请先上传。"}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
ok: true,
|
|
97
|
+
entry: {
|
|
98
|
+
fileName: normalizedFileName,
|
|
99
|
+
lineNumber: parsedLine,
|
|
100
|
+
columnNumber: parsedColumn,
|
|
101
|
+
},
|
|
102
|
+
contextLineRadius: parsedContextLineRadius,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type {ResolvedSourceMeta, TraceLocation} from "@/shared/trace-common";
|
|
2
|
+
|
|
3
|
+
export interface ResolveTraceInput {
|
|
4
|
+
entryFileName: string
|
|
5
|
+
entryLine: string
|
|
6
|
+
entryColumn: string
|
|
7
|
+
contextLineRadius?: string
|
|
8
|
+
mapFilePaths: string[]
|
|
9
|
+
maxSnippetPayloadBytes?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TraceHopPayload {
|
|
13
|
+
input: TraceLocation
|
|
14
|
+
output?: TraceLocation
|
|
15
|
+
mapFileName?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ResolveTraceMetaOutput {
|
|
19
|
+
traceData: TraceHopPayload[]
|
|
20
|
+
resolvedSourceMeta: ResolvedSourceMeta | null
|
|
21
|
+
canFetchSourceSnippet: boolean
|
|
22
|
+
message: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ResolveTraceSnippetOutput {
|
|
26
|
+
traceCode: string
|
|
27
|
+
resolvedSourceMeta: ResolvedSourceMeta | null
|
|
28
|
+
traceHighlightLines: number[]
|
|
29
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ResolveTraceInput,
|
|
3
|
+
ResolveTraceMetaOutput,
|
|
4
|
+
ResolveTraceSnippetOutput,
|
|
5
|
+
} from "@/shared/trace-contract";
|
|
6
|
+
|
|
7
|
+
export interface MapInputConfig {
|
|
8
|
+
entryLine: string
|
|
9
|
+
entryColumn: string
|
|
10
|
+
contextLineRadius: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ForgeKitBridge {
|
|
14
|
+
applyNodeMethod?: (method: string, ...args: string[]) => Promise<unknown>
|
|
15
|
+
openFileDialog?: (options: {
|
|
16
|
+
multiple?: boolean
|
|
17
|
+
filters?: {name: string; extensions: string[]}[]
|
|
18
|
+
}) => Promise<string | string[] | null>
|
|
19
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONTEXT_LINE_RADIUS,
|
|
3
|
+
createInitialOutputState,
|
|
4
|
+
patchOutputMessage,
|
|
5
|
+
validateResolveInput,
|
|
6
|
+
} from "@/shared/trace-common";
|
|
7
|
+
import type {
|
|
8
|
+
ResolveInputConfig,
|
|
9
|
+
ResolveInputValidation,
|
|
10
|
+
TraceLocation,
|
|
11
|
+
TraceOutputState as SharedTraceOutputState,
|
|
12
|
+
} from "@/shared/trace-common";
|
|
13
|
+
export type {ResolveInputConfig, ResolvedSourceMeta, TraceLocation} from "@/shared/trace-common";
|
|
14
|
+
|
|
15
|
+
export interface TraceHop {
|
|
16
|
+
input: TraceLocation
|
|
17
|
+
output?: TraceLocation
|
|
18
|
+
mapFileName?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type TraceOutputState = SharedTraceOutputState<TraceHop>
|
|
22
|
+
|
|
23
|
+
export interface SourceMapChainState {
|
|
24
|
+
chainMapSlots: ChainMapSlot[]
|
|
25
|
+
entryFileName: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ChainMapSlot {
|
|
29
|
+
id: string
|
|
30
|
+
mapFileName: string
|
|
31
|
+
mapFilePath?: string
|
|
32
|
+
error?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TraceDisplayHop {
|
|
36
|
+
input: TraceLocation
|
|
37
|
+
output?: TraceLocation
|
|
38
|
+
mapFileName?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class SourceMapUtils {
|
|
42
|
+
static readonly DEFAULT_CONTEXT_LINE_RADIUS = DEFAULT_CONTEXT_LINE_RADIUS
|
|
43
|
+
|
|
44
|
+
static createInitialOutputState(): TraceOutputState {
|
|
45
|
+
return createInitialOutputState<TraceHop>()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static patchOutputMessage(prev: TraceOutputState, message: string): TraceOutputState {
|
|
49
|
+
return patchOutputMessage(prev, message)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static removeChainSlot(slots: ChainMapSlot[], slotId: string): ChainMapSlot[] {
|
|
53
|
+
return slots.filter((item) => item.id !== slotId)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static reorderChainSlots(slots: ChainMapSlot[], nextIds: string[]): ChainMapSlot[] {
|
|
57
|
+
const idOrder = new Map(nextIds.map((id, index) => [id, index]))
|
|
58
|
+
return [...slots].sort((a, b) => {
|
|
59
|
+
const aIndex = idOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER
|
|
60
|
+
const bIndex = idOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER
|
|
61
|
+
return aIndex - bIndex
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static mapTraceForDisplay(traceData: TraceHop[]): TraceDisplayHop[] {
|
|
66
|
+
return traceData.map((item) => {
|
|
67
|
+
let output: TraceLocation | undefined
|
|
68
|
+
if (item.output) output = {...item.output}
|
|
69
|
+
return {
|
|
70
|
+
input: {...item.input},
|
|
71
|
+
output,
|
|
72
|
+
mapFileName: item.mapFileName,
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static validateResolveInput(config: ResolveInputConfig): ResolveInputValidation {
|
|
78
|
+
return validateResolveInput(config)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {normalizePath, stripMapExt} from "../base/path";
|
|
2
|
-
import {registerSourceMap} from "../base/registry";
|
|
3
|
-
import type {ChainMapSlot, SourceMapRegistry} from "../base/types";
|
|
4
|
-
|
|
5
|
-
export const createChainSlotFromFile = async (file: File): Promise<ChainMapSlot> => {
|
|
6
|
-
const slotId = `${file.name}-${Math.random().toString(36).slice(2, 8)}`
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
const fileText = await file.text()
|
|
10
|
-
const rawSourceMap = JSON.parse(fileText)
|
|
11
|
-
|
|
12
|
-
if (!rawSourceMap || typeof rawSourceMap !== "object" || typeof rawSourceMap.mappings !== "string") {
|
|
13
|
-
throw new Error("invalid source-map json")
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
id: slotId,
|
|
18
|
-
mapFileName: file.name,
|
|
19
|
-
sourceMapRecord: {rawSourceMap, mapFileName: file.name},
|
|
20
|
-
}
|
|
21
|
-
} catch (error) {
|
|
22
|
-
const message = error instanceof Error ? error.message : "unknown error"
|
|
23
|
-
return {
|
|
24
|
-
id: slotId,
|
|
25
|
-
mapFileName: file.name,
|
|
26
|
-
error: `${file.name}: ${message}`,
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const buildRegistryFromChainSlots = (slots: ChainMapSlot[]) => {
|
|
32
|
-
let registry: SourceMapRegistry = {}
|
|
33
|
-
const orderedMapFileNames: string[] = []
|
|
34
|
-
|
|
35
|
-
slots.forEach((slot) => {
|
|
36
|
-
const {sourceMapRecord: record} = slot
|
|
37
|
-
if (!record) return
|
|
38
|
-
|
|
39
|
-
orderedMapFileNames.push(record.mapFileName)
|
|
40
|
-
registry = registerSourceMap(registry, record.mapFileName, record)
|
|
41
|
-
registry = registerSourceMap(registry, stripMapExt(record.mapFileName), record)
|
|
42
|
-
|
|
43
|
-
if (record.rawSourceMap.file) {
|
|
44
|
-
registry = registerSourceMap(registry, record.rawSourceMap.file, record)
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
return {registry, orderedMapFileNames}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export const getAutoEntryFileNameFromSlots = (slots: ChainMapSlot[]) => {
|
|
52
|
-
const firstValidSlot = slots.find((slot) => slot.sourceMapRecord)
|
|
53
|
-
if (!firstValidSlot?.sourceMapRecord) return ""
|
|
54
|
-
|
|
55
|
-
const mappedFile = firstValidSlot.sourceMapRecord.rawSourceMap.file
|
|
56
|
-
if (mappedFile) return normalizePath(mappedFile)
|
|
57
|
-
|
|
58
|
-
return stripMapExt(firstValidSlot.sourceMapRecord.mapFileName)
|
|
59
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import {SourceMapConsumer, type RawSourceMap} from "source-map-js";
|
|
2
|
-
import {MAX_TRACE_DEPTH} from "../base/constants";
|
|
3
|
-
import {getSourceMapByFileName, getSourceMapByMapFileName} from "../base/registry";
|
|
4
|
-
import type {ResolveTraceOptions, SourceMapRecord, SourceMapRegistry, TraceHop, TraceLocation} from "../base/types";
|
|
5
|
-
|
|
6
|
-
interface ResolvedTraceHop extends TraceHop {
|
|
7
|
-
output: TraceLocation
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const toLocationCursor = (location: TraceLocation) => {
|
|
11
|
-
return `${location.fileName}:${location.lineNumber}:${location.columnNumber}`
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const isSameLocation = (left: TraceLocation, right: TraceLocation) => {
|
|
15
|
-
return (
|
|
16
|
-
left.fileName === right.fileName
|
|
17
|
-
&& left.lineNumber === right.lineNumber
|
|
18
|
-
&& left.columnNumber === right.columnNumber
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const resolveOriginalLocation = (
|
|
23
|
-
rawSourceMap: RawSourceMap,
|
|
24
|
-
position: { line: number; column: number },
|
|
25
|
-
): TraceLocation | null => {
|
|
26
|
-
const consumer = new SourceMapConsumer(rawSourceMap)
|
|
27
|
-
|
|
28
|
-
const attempts = [
|
|
29
|
-
{line: position.line, column: position.column, bias: SourceMapConsumer.GREATEST_LOWER_BOUND},
|
|
30
|
-
{line: position.line, column: 0, bias: SourceMapConsumer.GREATEST_LOWER_BOUND},
|
|
31
|
-
{line: position.line, column: position.column, bias: SourceMapConsumer.LEAST_UPPER_BOUND},
|
|
32
|
-
{line: position.line, column: 0, bias: SourceMapConsumer.LEAST_UPPER_BOUND},
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
for (const attempt of attempts) {
|
|
36
|
-
const found = consumer.originalPositionFor(attempt)
|
|
37
|
-
if (found.source && found.line != null && found.column != null) {
|
|
38
|
-
return {
|
|
39
|
-
fileName: found.source,
|
|
40
|
-
lineNumber: found.line,
|
|
41
|
-
columnNumber: found.column,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const resolveHopFromRecord = (sourceMapRecord: SourceMapRecord, input: TraceLocation): ResolvedTraceHop | null => {
|
|
50
|
-
try {
|
|
51
|
-
const output = resolveOriginalLocation(sourceMapRecord.rawSourceMap, {
|
|
52
|
-
line: input.lineNumber,
|
|
53
|
-
column: input.columnNumber,
|
|
54
|
-
})
|
|
55
|
-
if (!output) return null
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
input,
|
|
59
|
-
output,
|
|
60
|
-
mapFileName: sourceMapRecord.mapFileName,
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
return null
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const appendUnresolvedHop = (traceHops: TraceHop[], input: TraceLocation, mapFileName?: string) => {
|
|
68
|
-
traceHops.push({input, mapFileName})
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const resolveTraceWithOrderedMaps = (
|
|
72
|
-
entry: TraceLocation,
|
|
73
|
-
sourceMaps: SourceMapRegistry,
|
|
74
|
-
orderedMapFileNames: string[],
|
|
75
|
-
maxDepth: number,
|
|
76
|
-
) => {
|
|
77
|
-
const traceHops: TraceHop[] = []
|
|
78
|
-
const visited = new Set<string>()
|
|
79
|
-
let current = entry
|
|
80
|
-
|
|
81
|
-
for (let depth = 0; depth < orderedMapFileNames.length && depth < maxDepth; depth++) {
|
|
82
|
-
const mapFileName = orderedMapFileNames[depth]
|
|
83
|
-
const cursor = toLocationCursor(current)
|
|
84
|
-
|
|
85
|
-
if (visited.has(cursor)) {
|
|
86
|
-
appendUnresolvedHop(traceHops, current, mapFileName)
|
|
87
|
-
continue
|
|
88
|
-
}
|
|
89
|
-
visited.add(cursor)
|
|
90
|
-
|
|
91
|
-
const sourceMapRecord = getSourceMapByMapFileName(sourceMaps, mapFileName)
|
|
92
|
-
if (!sourceMapRecord) {
|
|
93
|
-
appendUnresolvedHop(traceHops, current, mapFileName)
|
|
94
|
-
continue
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const hop = resolveHopFromRecord(sourceMapRecord, current)
|
|
98
|
-
if (!hop) {
|
|
99
|
-
appendUnresolvedHop(traceHops, current, sourceMapRecord.mapFileName)
|
|
100
|
-
continue
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
traceHops.push(hop)
|
|
104
|
-
current = hop.output
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return traceHops
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const resolveTraceByFileLookup = (
|
|
111
|
-
entry: TraceLocation,
|
|
112
|
-
sourceMaps: SourceMapRegistry,
|
|
113
|
-
maxDepth: number,
|
|
114
|
-
) => {
|
|
115
|
-
const traceHops: TraceHop[] = []
|
|
116
|
-
const visited = new Set<string>()
|
|
117
|
-
let current = entry
|
|
118
|
-
|
|
119
|
-
for (let depth = 0; depth < maxDepth; depth++) {
|
|
120
|
-
const cursor = toLocationCursor(current)
|
|
121
|
-
|
|
122
|
-
if (visited.has(cursor)) {
|
|
123
|
-
appendUnresolvedHop(traceHops, current)
|
|
124
|
-
break
|
|
125
|
-
}
|
|
126
|
-
visited.add(cursor)
|
|
127
|
-
|
|
128
|
-
const sourceMapRecord = getSourceMapByFileName(sourceMaps, current.fileName)
|
|
129
|
-
if (!sourceMapRecord) {
|
|
130
|
-
if (traceHops.length === 0) {
|
|
131
|
-
appendUnresolvedHop(traceHops, current)
|
|
132
|
-
}
|
|
133
|
-
break
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const hop = resolveHopFromRecord(sourceMapRecord, current)
|
|
137
|
-
if (!hop) {
|
|
138
|
-
appendUnresolvedHop(traceHops, current, sourceMapRecord.mapFileName)
|
|
139
|
-
break
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
traceHops.push(hop)
|
|
143
|
-
if (isSameLocation(hop.output, current)) {
|
|
144
|
-
break
|
|
145
|
-
}
|
|
146
|
-
current = hop.output
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return traceHops
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export const resolveTraceBySourceMaps = async (
|
|
153
|
-
entry: TraceLocation,
|
|
154
|
-
sourceMaps: SourceMapRegistry,
|
|
155
|
-
orderedMapFileNames: string[] = [],
|
|
156
|
-
options: ResolveTraceOptions = {},
|
|
157
|
-
): Promise<TraceHop[]> => {
|
|
158
|
-
const maxDepth = options.maxDepth ?? MAX_TRACE_DEPTH
|
|
159
|
-
|
|
160
|
-
if (orderedMapFileNames.length > 0) {
|
|
161
|
-
return resolveTraceWithOrderedMaps(entry, sourceMaps, orderedMapFileNames, maxDepth)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return resolveTraceByFileLookup(entry, sourceMaps, maxDepth)
|
|
165
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {DEFAULT_CONTEXT_LINE_RADIUS} from "../base/constants";
|
|
2
|
-
import type {TraceLocation} from "../base/types";
|
|
3
|
-
|
|
4
|
-
export const toDisplayLocation = (location: TraceLocation): TraceLocation => ({...location})
|
|
5
|
-
|
|
6
|
-
export const createCodeExcerpt = (fullCode: string, targetLine: number, radius = DEFAULT_CONTEXT_LINE_RADIUS) => {
|
|
7
|
-
const lines = fullCode.split(/\r?\n/)
|
|
8
|
-
if (lines.length === 0) {
|
|
9
|
-
return {code: fullCode, highlightLine: targetLine}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const safeTarget = Math.min(Math.max(targetLine, 1), lines.length)
|
|
13
|
-
const start = Math.max(1, safeTarget - radius)
|
|
14
|
-
const end = Math.min(lines.length, safeTarget + radius)
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
code: lines.slice(start - 1, end).join("\n"),
|
|
18
|
-
highlightLine: safeTarget - start + 1,
|
|
19
|
-
}
|
|
20
|
-
}
|