Skip to content

Commit bc5a060

Browse files
committed
fix: gracefully handle syntax errors in definePage
Fix #692, #691
1 parent f4a845a commit bc5a060

File tree

2 files changed

+102
-12
lines changed

2 files changed

+102
-12
lines changed

src/core/definePage.spec.ts

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TransformResult } from 'vite'
22
import { expect, describe, it } from 'vitest'
33
import { definePageTransform, extractDefinePageInfo } from './definePage'
44
import { ts } from '../utils'
5+
import { mockWarn } from '../../tests/vitest-mock-warn'
56

67
const vue = String.raw
78

@@ -21,6 +22,7 @@ const b = 1
2122
`
2223

2324
describe('definePage', () => {
25+
mockWarn()
2426
it('removes definePage', async () => {
2527
const result = (await definePageTransform({
2628
code: sampleCode,
@@ -156,22 +158,25 @@ definePage({
156158
expect(result?.code).toMatchSnapshot()
157159
})
158160

159-
it('throws if definePage uses a variable from the setup', async () => {
160-
const code = vue`
161+
it('handles definePage using a variable from setup gracefully', async () => {
162+
const code = `
161163
<script setup>
162164
const a = 1
163165
definePage({
164166
name: a,
165167
})
166168
</script>
167169
`
168-
// the function syntax works with sync and async errors
169-
await expect(async () => {
170-
await definePageTransform({
171-
code,
172-
id: 'src/pages/basic.vue&definePage&vue',
173-
})
174-
}).rejects.toThrowError()
170+
const result = await definePageTransform({
171+
code,
172+
id: 'src/pages/basic.vue&definePage&vue',
173+
})
174+
175+
// Should return empty object instead of throwing
176+
expect(result).toBe('export default {}')
177+
expect(
178+
'`definePage()` in <script setup> cannot reference locally declared variables'
179+
).toHaveBeenWarned()
175180
})
176181

177182
it('extracts name and path', () => {
@@ -312,4 +317,52 @@ export default {
312317
path: '/custom',
313318
})
314319
})
320+
321+
describe('error handling', () => {
322+
const codeWithSyntaxError = `
323+
<script setup>
324+
definePage({
325+
name: 'test',, // syntax error: extra comma
326+
path: '/test'
327+
})
328+
</script>
329+
330+
<template>
331+
<div>hello</div>
332+
</template>
333+
`
334+
335+
it('handles syntax errors gracefully when extracting definePage', async () => {
336+
const result = await definePageTransform({
337+
code: codeWithSyntaxError,
338+
id: 'src/pages/broken.vue?definePage&vue',
339+
})
340+
341+
// Should return empty object instead of crashing
342+
expect(result).toBe('export default {}')
343+
expect('Failed to process definePage:').toHaveBeenWarned()
344+
})
345+
346+
it('handles syntax errors gracefully when removing definePage from source', async () => {
347+
const result = await definePageTransform({
348+
code: codeWithSyntaxError,
349+
id: 'src/pages/broken.vue',
350+
})
351+
352+
// Should return undefined (no transform) instead of crashing
353+
expect(result).toBeUndefined()
354+
expect('Failed to process definePage:').toHaveBeenWarned()
355+
})
356+
357+
it('handles extractDefinePageNameAndPath with syntax errors gracefully', async () => {
358+
const result = extractDefinePageInfo(
359+
codeWithSyntaxError,
360+
'src/pages/broken.vue'
361+
)
362+
363+
// Should return null/undefined instead of crashing
364+
expect(result).toBeUndefined()
365+
expect('Failed to extract definePage info:').toHaveBeenWarned()
366+
})
367+
})
315368
})

src/core/definePage.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,23 @@ export function definePageTransform({
8080
return isExtractingDefinePage ? 'export default {}' : undefined
8181
}
8282

83-
const { ast, offset, definePageNodes } = getCodeAst(code, id)
83+
let ast: Program | undefined
84+
let offset: number
85+
let definePageNodes: CallExpression[]
86+
87+
try {
88+
const result = getCodeAst(code, id)
89+
ast = result.ast
90+
offset = result.offset
91+
definePageNodes = result.definePageNodes
92+
} catch (error) {
93+
// Handle any syntax errors or parsing errors gracefully
94+
warn(
95+
`[${id}]: Failed to process definePage: ${error instanceof Error ? error.message : 'Unknown error'}`
96+
)
97+
return isExtractingDefinePage ? 'export default {}' : undefined
98+
}
99+
84100
if (!ast) return
85101

86102
if (!definePageNodes.length) {
@@ -111,7 +127,14 @@ export function definePageTransform({
111127
const scriptBindings = ast.body ? getIdentifiers(ast.body) : []
112128

113129
// this will throw if a property from the script setup is used in definePage
114-
checkInvalidScopeReference(routeRecord, MACRO_DEFINE_PAGE, scriptBindings)
130+
try {
131+
checkInvalidScopeReference(routeRecord, MACRO_DEFINE_PAGE, scriptBindings)
132+
} catch (error) {
133+
warn(
134+
`[${id}]: ${error instanceof Error ? error.message : 'Invalid scope reference in definePage'}`
135+
)
136+
return 'export default {}'
137+
}
115138

116139
s.remove(offset + routeRecord.end!, code.length)
117140
s.remove(0, offset + routeRecord.start!)
@@ -203,7 +226,21 @@ export function extractDefinePageInfo(
203226
): DefinePageInfo | null | undefined {
204227
if (!sfcCode.includes(MACRO_DEFINE_PAGE)) return
205228

206-
const { ast, definePageNodes } = getCodeAst(sfcCode, id)
229+
let ast: Program | undefined
230+
let definePageNodes: CallExpression[]
231+
232+
try {
233+
const result = getCodeAst(sfcCode, id)
234+
ast = result.ast
235+
definePageNodes = result.definePageNodes
236+
} catch (error) {
237+
// Handle any syntax errors or parsing errors gracefully
238+
warn(
239+
`[${id}]: Failed to extract definePage info: ${error instanceof Error ? error.message : 'Unknown error'}`
240+
)
241+
return undefined
242+
}
243+
207244
if (!ast) return
208245

209246
if (!definePageNodes.length) {

0 commit comments

Comments
 (0)