diff --git a/playground/typed-router.d.ts b/playground/typed-router.d.ts index c4b6e56f2..896141e31 100644 --- a/playground/typed-router.d.ts +++ b/playground/typed-router.d.ts @@ -63,6 +63,7 @@ declare module 'vue-router/auto-routes' { '/nested-group/(nested-group-first-level)/nested-group-first-level-child': RouteRecordInfo<'/nested-group/(nested-group-first-level)/nested-group-first-level-child', '/nested-group/nested-group-first-level-child', Record, Record>, '/partial-[name]': RouteRecordInfo<'/partial-[name]', '/partial-:name', { name: ParamValue }, { name: ParamValue }>, '/custom-path': RouteRecordInfo<'/custom-path', '/surprise-:id(\d+)', Record, Record>, + '/syntax-error-test': RouteRecordInfo<'/syntax-error-test', '/syntax-error-test', Record, Record>, '/test-[a-id]': RouteRecordInfo<'/test-[a-id]', '/test-:a-id', { aId: ParamValue }, { aId: ParamValue }>, '/todos/': RouteRecordInfo<'/todos/', '/todos', Record, Record>, '/todos/+layout': RouteRecordInfo<'/todos/+layout', '/todos/+layout', Record, Record>, @@ -283,6 +284,10 @@ declare module 'vue-router/auto-routes' { routes: '/custom-path' views: never } + 'src/pages/syntax-error-test.vue': { + routes: '/syntax-error-test' + views: never + } 'src/pages/test-[a-id].vue': { routes: '/test-[a-id]' views: never diff --git a/src/core/definePage.spec.ts b/src/core/definePage.spec.ts index 07d92d4f8..0d70d0b83 100644 --- a/src/core/definePage.spec.ts +++ b/src/core/definePage.spec.ts @@ -254,4 +254,72 @@ export default { path: '/custom', }) }) + + it('handles syntax errors gracefully', async () => { + const invalidCode = ` + + + + ` + + // Should not throw and return undefined for normal transform + const result = await definePageTransform({ + code: invalidCode, + id: 'src/pages/invalid.vue', + }) + expect(result).toBeUndefined() + + // Should return empty object for definePage extraction + const extractResult = await definePageTransform({ + code: invalidCode, + id: 'src/pages/invalid.vue?definePage&vue', + }) + expect(extractResult).toBe('export default {}') + + // extractDefinePageNameAndPath should also handle syntax errors gracefully + const nameAndPath = await extractDefinePageNameAndPath(invalidCode, 'src/pages/invalid.vue') + expect(nameAndPath).toBeUndefined() + }) + + it('handles various syntax errors in definePage', async () => { + // Test with missing closing brace + const invalidCode1 = ` + + + + ` + + const result1 = await definePageTransform({ + code: invalidCode1, + id: 'src/pages/invalid1.vue', + }) + expect(result1).toBeUndefined() + + // Test with invalid property syntax + const invalidCode2 = ` + + + + ` + + const result2 = await definePageTransform({ + code: invalidCode2, + id: 'src/pages/invalid2.vue', + }) + expect(result2).toBeUndefined() + + const extractResult2 = await definePageTransform({ + code: invalidCode2, + id: 'src/pages/invalid2.vue?definePage&vue', + }) + expect(extractResult2).toBe('export default {}') + }) }) diff --git a/src/core/definePage.ts b/src/core/definePage.ts index ca420eb1e..5e8a38b98 100644 --- a/src/core/definePage.ts +++ b/src/core/definePage.ts @@ -35,17 +35,26 @@ function getCodeAst(code: string, id: string) { let offset = 0 let ast: Program | undefined const lang = getLang(id.split(MACRO_DEFINE_PAGE_QUERY)[0]!) - if (lang === 'vue') { - const sfc = parseSFC(code, id) - if (sfc.scriptSetup) { - ast = sfc.getSetupAst() - offset = sfc.scriptSetup.loc.start.offset - } else if (sfc.script) { - ast = sfc.getScriptAst() - offset = sfc.script.loc.start.offset + + try { + if (lang === 'vue') { + const sfc = parseSFC(code, id) + if (sfc.scriptSetup) { + ast = sfc.getSetupAst() + offset = sfc.scriptSetup.loc.start.offset + } else if (sfc.script) { + ast = sfc.getScriptAst() + offset = sfc.script.loc.start.offset + } + } else if (/[jt]sx?$/.test(lang)) { + ast = babelParse(code, lang) } - } else if (/[jt]sx?$/.test(lang)) { - ast = babelParse(code, lang) + } catch (error) { + // If there's a syntax error in the code, warn and return empty results + // This prevents crashing the dev server when there are syntax errors + const errorMessage = error instanceof Error ? error.message : String(error) + warn(`[${id}]: Failed to parse code due to syntax error: ${errorMessage}`) + return { ast: undefined, offset: 0, definePageNodes: [] } } const definePageNodes: CallExpression[] = (ast?.body || []) @@ -78,7 +87,10 @@ export function definePageTransform({ } const { ast, offset, definePageNodes } = getCodeAst(code, id) - if (!ast) return + if (!ast) { + // If parsing failed but we're extracting definePage, return empty object + return isExtractingDefinePage ? 'export default {}' : undefined + } if (!definePageNodes.length) { return isExtractingDefinePage