Skip to content

Commit 357b8fe

Browse files
authored
fix: return error instead of panic for missing frontmatter fence (#1137)
* fix: return error instead of panic for missing frontmatter fence When a file has a closing frontmatter fence (---) without an opening fence, the parser would panic with 'originalIM was set twice'. This change detects that condition and returns a helpful diagnostic error instead. Fixes #1133, #1038. May also help with #1094, #1107. * Add test for location
1 parent cba568f commit 357b8fe

File tree

5 files changed

+64
-0
lines changed

5 files changed

+64
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@astrojs/compiler": patch
3+
---
4+
5+
Fixes a panic when parsing files with a closing frontmatter fence (---) but no opening fence. The compiler now returns a helpful diagnostic error instead of crashing.

internal/loc/diagnostics.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const (
99
ERROR_UNMATCHED_IMPORT DiagnosticCode = 1003
1010
ERROR_UNSUPPORTED_SLOT_ATTRIBUTE DiagnosticCode = 1004
1111
ERROR_UNTERMINATED_STRING DiagnosticCode = 1005
12+
ERROR_MISSING_FRONTMATTER_FENCE DiagnosticCode = 1006
1213
WARNING DiagnosticCode = 2000
1314
WARNING_UNTERMINATED_HTML_COMMENT DiagnosticCode = 2001
1415
WARNING_UNCLOSED_HTML_TAG DiagnosticCode = 2002

internal/parser.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,19 @@ func copyAttributes(dst *Node, src Token) {
11031103
func inBodyIM(p *parser) bool {
11041104
switch p.tok.Type {
11051105
case FrontmatterFenceToken:
1106+
// If originalIM is already set, we have a closing fence without an opening one
1107+
if p.originalIM != nil {
1108+
p.handler.AppendError(&loc.ErrorWithRange{
1109+
Code: loc.ERROR_MISSING_FRONTMATTER_FENCE,
1110+
Text: "The closing frontmatter fence (---) is missing an opening fence",
1111+
Hint: "Add --- at the beginning of your file before any import statements or code",
1112+
Range: loc.Range{
1113+
Loc: p.tok.Loc,
1114+
Len: 3,
1115+
},
1116+
})
1117+
return true
1118+
}
11061119
p.setOriginalIM()
11071120
p.im = frontmatterIM
11081121
return false

internal/token.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,7 @@ frontmatter_loop:
18861886
case FrontmatterInitial:
18871887
z.fm = FrontmatterOpen
18881888
z.dashCount = 0
1889+
z.data.Start = z.raw.End - len("---")
18891890
z.data.End = z.raw.End
18901891
z.tt = FrontmatterFenceToken
18911892
z.openBraceIsExpressionStart = false
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { type TransformResult, transform } from '@astrojs/compiler';
2+
import { test } from 'uvu';
3+
import * as assert from 'uvu/assert';
4+
5+
// Missing opening frontmatter fence - only has closing ---
6+
const FIXTURE = `import BaseLayout from '@/layouts/BaseLayout.astro';
7+
import { getCollection } from 'astro:content';
8+
const posts = await getCollection('blog');
9+
---
10+
<BaseLayout title="Crash Test">
11+
<h1>{posts.length}</h1>
12+
</BaseLayout>`;
13+
14+
let result: TransformResult;
15+
test.before(async () => {
16+
result = await transform(FIXTURE, {
17+
filename: '/src/pages/checkthis.astro',
18+
});
19+
});
20+
21+
test('missing opening frontmatter fence reports error instead of panic', () => {
22+
assert.ok(Array.isArray(result.diagnostics));
23+
assert.is(result.diagnostics.length, 1);
24+
assert.is(result.diagnostics[0].code, 1006);
25+
assert.is(
26+
result.diagnostics[0].text,
27+
'The closing frontmatter fence (---) is missing an opening fence'
28+
);
29+
assert.is(
30+
result.diagnostics[0].hint,
31+
'Add --- at the beginning of your file before any import statements or code'
32+
);
33+
// Verify the error location points to the closing --- fence
34+
const loc = result.diagnostics[0].location;
35+
// The line number should point to the line containing ---
36+
assert.is(FIXTURE.split('\n')[loc.line - 1], '---');
37+
// The column and length should extract exactly the --- characters
38+
assert.is(
39+
FIXTURE.split('\n')[loc.line - 1].slice(loc.column - 1, loc.column - 1 + loc.length),
40+
'---'
41+
);
42+
});
43+
44+
test.run();

0 commit comments

Comments
 (0)