Skip to content

Commit 91dcf28

Browse files
committed
improve error info
1 parent e6fb373 commit 91dcf28

File tree

4 files changed

+86
-64
lines changed

4 files changed

+86
-64
lines changed

src/astro/index.ts

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type {
66
ParentNode,
77
TagLikeNode,
88
} from "@astrojs/compiler/types"
9+
import type { Context } from "../context"
10+
import { ParseError } from "../errors"
911

1012
/**
1113
* Checks if the given node is TagLikeNode
@@ -67,20 +69,20 @@ export function walk(
6769
/**
6870
* Get end offset of start tag
6971
*/
70-
export function getStartTagEndOffset(node: TagLikeNode, code: string): number {
72+
export function getStartTagEndOffset(node: TagLikeNode, ctx: Context): number {
7173
const lastAttr = node.attributes[node.attributes.length - 1]
7274
let beforeCloseIndex: number
7375
if (lastAttr) {
74-
beforeCloseIndex = getAttributeEndOffset(lastAttr, code)
76+
beforeCloseIndex = getAttributeEndOffset(lastAttr, ctx)
7577
} else {
7678
const info = getTokenInfo(
77-
code,
79+
ctx,
7880
[`<${node.name}`],
7981
node.position!.start.offset,
8082
)
8183
beforeCloseIndex = info.index + info.match.length
8284
}
83-
const info = getTokenInfo(code, [[">", "/>"]], beforeCloseIndex)
85+
const info = getTokenInfo(ctx, [[">", "/>"]], beforeCloseIndex)
8486
return info.index + info.match.length
8587
}
8688

@@ -89,43 +91,47 @@ export function getStartTagEndOffset(node: TagLikeNode, code: string): number {
8991
*/
9092
export function getAttributeEndOffset(
9193
node: AttributeNode,
92-
code: string,
94+
ctx: Context,
9395
): number {
9496
let info
9597
if (node.kind === "empty") {
96-
info = getTokenInfo(code, [node.name], node.position!.start.offset)
98+
info = getTokenInfo(ctx, [node.name], node.position!.start.offset)
9799
} else if (node.kind === "quoted") {
98100
info = getTokenInfo(
99-
code,
101+
ctx,
100102
[[`"${node.value}"`, `'${node.value}'`, node.value]],
101-
getAttributeValueStartOffset(node, code),
103+
getAttributeValueStartOffset(node, ctx),
102104
)
103105
} else if (node.kind === "expression") {
104106
info = getTokenInfo(
105-
code,
107+
ctx,
106108
["{", node.value, "}"],
107-
getAttributeValueStartOffset(node, code),
109+
getAttributeValueStartOffset(node, ctx),
108110
)
109111
} else if (node.kind === "shorthand") {
110112
info = getTokenInfo(
111-
code,
113+
ctx,
112114
["{", node.name, "}"],
113115
node.position!.start.offset,
114116
)
115117
} else if (node.kind === "spread") {
116118
info = getTokenInfo(
117-
code,
119+
ctx,
118120
["{", "...", node.name, "}"],
119121
node.position!.start.offset,
120122
)
121123
} else if (node.kind === "template-literal") {
122124
info = getTokenInfo(
123-
code,
125+
ctx,
124126
[`\`${node.value}\``],
125-
getAttributeValueStartOffset(node, code),
127+
getAttributeValueStartOffset(node, ctx),
126128
)
127129
} else {
128-
throw new Error(`Unknown attr kind: ${node.kind}`)
130+
throw new ParseError(
131+
`Unknown attr kind: ${node.kind}`,
132+
node.position!.start.offset,
133+
ctx,
134+
)
129135
}
130136
return info.index + info.match.length
131137
}
@@ -135,39 +141,43 @@ export function getAttributeEndOffset(
135141
*/
136142
export function getAttributeValueStartOffset(
137143
node: AttributeNode,
138-
code: string,
144+
ctx: Context,
139145
): number {
140146
let info
141147
if (node.kind === "quoted") {
142148
info = getTokenInfo(
143-
code,
149+
ctx,
144150
[node.name, "=", [`"`, `'`, node.value]],
145151
node.position!.start.offset,
146152
)
147153
} else if (node.kind === "expression") {
148154
info = getTokenInfo(
149-
code,
155+
ctx,
150156
[node.name, "=", "{"],
151157
node.position!.start.offset,
152158
)
153159
} else if (node.kind === "template-literal") {
154160
info = getTokenInfo(
155-
code,
161+
ctx,
156162
[node.name, "=", "`"],
157163
node.position!.start.offset,
158164
)
159165
} else {
160-
throw new Error(`Unknown attr kind: ${node.kind}`)
166+
throw new ParseError(
167+
`Unknown attr kind: ${node.kind}`,
168+
node.position!.start.offset,
169+
ctx,
170+
)
161171
}
162172
return info.index
163173
}
164174

165175
/**
166176
* Get end offset of comment
167177
*/
168-
export function getCommentEndOffset(node: CommentNode, code: string): number {
178+
export function getCommentEndOffset(node: CommentNode, ctx: Context): number {
169179
const info = getTokenInfo(
170-
code,
180+
ctx,
171181
["<!--", node.value, "-->"],
172182
node.position!.start.offset,
173183
)
@@ -179,7 +189,7 @@ export function getCommentEndOffset(node: CommentNode, code: string): number {
179189
* Get token info
180190
*/
181191
function getTokenInfo(
182-
string: string,
192+
ctx: Context,
183193
tokens: (string | string[])[],
184194
position: number,
185195
): {
@@ -201,10 +211,14 @@ function getTokenInfo(
201211
? matchOfStr(t, index)
202212
: matchOfForMulti(t, index)
203213
if (m == null) {
204-
throw new Error(
214+
throw new ParseError(
205215
`Unknown token at ${index}, expected: ${JSON.stringify(
206216
t,
207-
)}, actual: ${JSON.stringify(string.slice(index, index + 10))}`,
217+
)}, actual: ${JSON.stringify(
218+
ctx.code.slice(index, index + 10),
219+
)}`,
220+
index,
221+
ctx,
208222
)
209223
}
210224
lastMatch = m
@@ -216,8 +230,8 @@ function getTokenInfo(
216230
*/
217231
function matchOfStr(search: string, position: number) {
218232
const index =
219-
search.trim() === search ? skipSpaces(string, position) : position
220-
if (string.startsWith(search, index)) {
233+
search.trim() === search ? skipSpaces(ctx.code, position) : position
234+
if (ctx.code.startsWith(search, index)) {
221235
return {
222236
match: search,
223237
index,

src/parser/astro-parser/parse.ts

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,36 @@ import {
1212
skipSpaces,
1313
walk,
1414
} from "../../astro"
15+
import type { Context } from "../../context"
16+
import { ParseError } from "../../errors"
1517

1618
/**
1719
* Parse code by `@astrojs/compiler`
1820
*/
19-
export function parse(code: string): ParseResult {
21+
export function parse(code: string, ctx: Context): ParseResult {
2022
const ast = service.parse(code, { position: true }).ast
21-
fixLocations(ast, code)
23+
fixLocations(ast, ctx)
2224
return { ast }
2325
}
2426

2527
/**
2628
* Fix locations
2729
*/
28-
function fixLocations(node: ParentNode, code: string): void {
30+
function fixLocations(node: ParentNode, ctx: Context): void {
2931
// FIXME: Adjust because the parser does not return the correct location.
3032
let start = 0
3133
walk(
3234
node,
33-
code,
35+
ctx.code,
3436
(node) => {
3537
if (node.type === "frontmatter") {
3638
start = node.position!.start.offset = tokenIndex(
37-
code,
39+
ctx,
3840
"---",
3941
start,
4042
)
4143
start = node.position!.end!.offset =
42-
tokenIndex(code, "---", start + 3 + node.value.length) + 3
44+
tokenIndex(ctx, "---", start + 3 + node.value.length) + 3
4345
} else if (
4446
node.type === "fragment" ||
4547
node.type === "element" ||
@@ -50,31 +52,31 @@ function fixLocations(node: ParentNode, code: string): void {
5052
node.position = { start: {}, end: {} } as any
5153
}
5254
start = node.position!.start.offset = tokenIndex(
53-
code,
55+
ctx,
5456
"<",
5557
start,
5658
)
5759
start += 1
5860
start += node.name.length
5961
if (!node.attributes.length) {
60-
start = getStartTagEndOffset(node, code)
62+
start = getStartTagEndOffset(node, ctx)
6163
}
6264
} else if (node.type === "attribute") {
63-
fixLocationForAttr(node, code, start)
64-
start = getAttributeEndOffset(node, code)
65+
fixLocationForAttr(node, ctx, start)
66+
start = getAttributeEndOffset(node, ctx)
6567
} else if (node.type === "comment") {
66-
node.position!.start.offset = tokenIndex(code, "<!--", start)
67-
start = getCommentEndOffset(node, code)
68+
node.position!.start.offset = tokenIndex(ctx, "<!--", start)
69+
start = getCommentEndOffset(node, ctx)
6870
} else if (node.type === "text") {
6971
start = node.position!.start.offset = tokenIndex(
70-
code,
72+
ctx,
7173
node.value,
7274
start,
7375
)
7476
start += node.value.length
7577
} else if (node.type === "expression") {
7678
start = node.position!.start.offset = tokenIndex(
77-
code,
79+
ctx,
7880
"{",
7981
start,
8082
)
@@ -87,13 +89,13 @@ function fixLocations(node: ParentNode, code: string): void {
8789
node.position!.end = {} as any
8890
}
8991
start = node.position!.start.offset = tokenIndex(
90-
code,
92+
ctx,
9193
"<!",
9294
start,
9395
)
9496
start += 2
9597
start = node.position!.end!.offset =
96-
code.indexOf(">", start) + 1
98+
ctx.code.indexOf(">", start) + 1
9799
} else if (node.type === "root") {
98100
// noop
99101
}
@@ -102,26 +104,26 @@ function fixLocations(node: ParentNode, code: string): void {
102104
if (node.type === "attribute") {
103105
const attributes = (parent as TagLikeNode).attributes
104106
if (attributes[attributes.length - 1] === node) {
105-
start = getStartTagEndOffset(parent as TagLikeNode, code)
107+
start = getStartTagEndOffset(parent as TagLikeNode, ctx)
106108
}
107109
return
108110
}
109111
if (node.type === "expression") {
110-
start = tokenIndex(code, "}", start) + 1
112+
start = tokenIndex(ctx, "}", start) + 1
111113
} else if (
112114
node.type === "fragment" ||
113115
node.type === "element" ||
114116
node.type === "component" ||
115117
node.type === "custom-element"
116118
) {
117119
const closeTagStart = tokenIndexSafe(
118-
code,
120+
ctx.code,
119121
`</${node.name}`,
120122
start,
121123
)
122124
if (closeTagStart != null) {
123125
start = closeTagStart + 2 + node.name.length
124-
start = tokenIndex(code, ">", start) + 1
126+
start = tokenIndex(ctx, ">", start) + 1
125127
}
126128
} else {
127129
return
@@ -136,36 +138,42 @@ function fixLocations(node: ParentNode, code: string): void {
136138
/**
137139
* Fix locations
138140
*/
139-
function fixLocationForAttr(node: AttributeNode, code: string, start: number) {
141+
function fixLocationForAttr(node: AttributeNode, ctx: Context, start: number) {
140142
if (node.kind === "empty") {
141-
node.position!.start.offset = tokenIndex(code, node.name, start)
143+
node.position!.start.offset = tokenIndex(ctx, node.name, start)
142144
} else if (node.kind === "quoted") {
143-
node.position!.start.offset = tokenIndex(code, node.name, start)
145+
node.position!.start.offset = tokenIndex(ctx, node.name, start)
144146
} else if (node.kind === "expression") {
145-
node.position!.start.offset = tokenIndex(code, node.name, start)
147+
node.position!.start.offset = tokenIndex(ctx, node.name, start)
146148
} else if (node.kind === "shorthand") {
147-
node.position!.start.offset = tokenIndex(code, "{", start)
149+
node.position!.start.offset = tokenIndex(ctx, "{", start)
148150
} else if (node.kind === "spread") {
149-
node.position!.start.offset = tokenIndex(code, "{", start)
151+
node.position!.start.offset = tokenIndex(ctx, "{", start)
150152
} else if (node.kind === "template-literal") {
151-
node.position!.start.offset = tokenIndex(code, node.name, start)
153+
node.position!.start.offset = tokenIndex(ctx, node.name, start)
152154
} else {
153-
throw new Error(`Unknown attr kind: ${node.kind}`)
155+
throw new ParseError(
156+
`Unknown attr kind: ${node.kind}`,
157+
node.position!.start.offset,
158+
ctx,
159+
)
154160
}
155161
}
156162

157163
/**
158164
* Get token index
159165
*/
160-
function tokenIndex(string: string, token: string, position: number): number {
161-
const index = tokenIndexSafe(string, token, position)
166+
function tokenIndex(ctx: Context, token: string, position: number): number {
167+
const index = tokenIndexSafe(ctx.code, token, position)
162168
if (index == null) {
163169
const start =
164-
token.trim() === token ? skipSpaces(string, position) : position
165-
throw new Error(
170+
token.trim() === token ? skipSpaces(ctx.code, position) : position
171+
throw new ParseError(
166172
`Unknown token at ${start}, expected: ${JSON.stringify(
167173
token,
168-
)}, actual: ${JSON.stringify(string.slice(start, start + 10))}`,
174+
)}, actual: ${JSON.stringify(ctx.code.slice(start, start + 10))}`,
175+
start,
176+
ctx,
169177
)
170178
}
171179
return index

src/parser/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function extractTokens(ast: ESLintExtendedProgram, ctx: Context) {
128128
*/
129129
export function parseTemplate(code: string, ctx: Context): ParseResult {
130130
try {
131-
return parseAstro(code)
131+
return parseAstro(code, ctx)
132132
} catch (e: any) {
133133
if (typeof e.pos === "number") {
134134
const err = new ParseError(e.message, e.pos, ctx)

src/parser/process-template.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ export function processTemplate(
176176
})
177177
} else if (attr.kind === "template-literal") {
178178
const attrStart = attr.position!.start.offset
179-
const start = getAttributeValueStartOffset(attr, ctx.code)
180-
const end = getAttributeEndOffset(attr, ctx.code)
179+
const start = getAttributeValueStartOffset(attr, ctx)
180+
const end = getAttributeEndOffset(attr, ctx)
181181
script.appendOriginal(start)
182182
script.appendScript("{")
183183
script.appendOriginal(end)
@@ -340,7 +340,7 @@ function getVoidSelfClosingTag(
340340
const next = parent.children[childIndex + 1]
341341
nextElementIndex = next.position!.start.offset
342342
}
343-
const endOffset = getStartTagEndOffset(node, code)
343+
const endOffset = getStartTagEndOffset(node, ctx)
344344
if (code.slice(endOffset, nextElementIndex).trim()) {
345345
// has end tag
346346
return null

0 commit comments

Comments
 (0)