-
-
Notifications
You must be signed in to change notification settings - Fork 79
/
Copy pathfix-locations.ts
132 lines (119 loc) · 4.21 KB
/
fix-locations.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import type {
ESLintExtendedProgram,
ESLintNode,
HasLocation,
LocationRange,
Node,
ParseError,
} from "../ast"
import { traverseNodes } from "../ast"
import type { LocationCalculator } from "./location-calculator"
/**
* Do post-process of parsing an expression.
*
* 1. Set `node.parent`.
* 2. Fix `node.range` and `node.loc` for HTML entities.
*
* @param result The parsing result to modify.
* @param locationCalculator The location calculator to modify.
*/
export function fixLocations(
result: ESLintExtendedProgram,
locationCalculator: LocationCalculator,
): void {
fixNodeLocations(result.ast, result.visitorKeys, locationCalculator)
for (const token of result.ast.tokens || []) {
fixLocation(token, locationCalculator)
}
for (const comment of result.ast.comments || []) {
fixLocation(comment, locationCalculator)
}
}
export function fixNodeLocations(
rootNode: ESLintNode,
visitorKeys: ESLintExtendedProgram["visitorKeys"],
locationCalculator: LocationCalculator,
): void {
// There are cases which the same node instance appears twice in the tree.
// E.g. `let {a} = {}` // This `a` appears twice at `Property#key` and `Property#value`.
const traversed = new Map<Node | number[] | LocationRange, Node>()
traverseNodes(rootNode, {
visitorKeys,
enterNode(node, parent) {
if (!traversed.has(node)) {
traversed.set(node, node)
node.parent = parent
// `babel-eslint@8` has shared `Node#range` with multiple nodes.
// See also: https://github.com/vuejs/eslint-plugin-vue/issues/208
if (traversed.has(node.range)) {
if (!traversed.has(node.loc)) {
// However, `Node#loc` may not be shared.
// See also: https://github.com/vuejs/vue-eslint-parser/issues/84
node.loc.start = locationCalculator.getLocFromIndex(
node.range[0],
)
node.loc.end = locationCalculator.getLocFromIndex(
node.range[1],
)
traversed.set(node.loc, node)
} else if (node.start != null || node.end != null) {
const traversedNode = traversed.get(node.range)!
if (traversedNode.type === node.type) {
node.start = traversedNode.start
node.end = traversedNode.end
}
}
} else {
fixLocation(node, locationCalculator)
traversed.set(node.range, node)
traversed.set(node.loc, node)
}
}
},
leaveNode() {
// Do nothing.
},
})
}
/**
* Modify the location information of the given node with using the base offset and gaps of this calculator.
* @param node The node to modify their location.
*/
export function fixLocation<T extends HasLocation>(
node: T,
locationCalculator: LocationCalculator,
): T {
const range = node.range
const loc = node.loc
const d0 = locationCalculator.getFixOffset(range[0], "start")
const d1 = locationCalculator.getFixOffset(range[1], "end")
if (d0 !== 0) {
range[0] += d0
if (node.start != null) {
node.start += d0
}
loc.start = locationCalculator.getLocFromIndex(range[0])
}
if (d1 !== 0) {
range[1] += d1
if (node.end != null) {
node.end += d0
}
loc.end = locationCalculator.getLocFromIndex(range[1])
}
return node
}
/**
* Modify the location information of the given error with using the base offset and gaps of this calculator.
* @param error The error to modify their location.
*/
export function fixErrorLocation(
error: ParseError,
locationCalculator: LocationCalculator,
) {
const diff = locationCalculator.getFixOffset(error.index, "start")
error.index += diff
const loc = locationCalculator.getLocFromIndex(error.index)
error.lineNumber = loc.line
error.column = loc.column
}