From: Eduardo San Martin Morote Date: Thu, 16 Jul 2026 15:10:43 +0000 (+0200) Subject: feat(unplugin): don't crash on duplicate definePage() calls (#2753) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=686bf9e7e84b083dec151ca09bbe77bcef3034f4;p=thirdparty%2Fvuejs%2Frouter.git feat(unplugin): don't crash on duplicate definePage() calls (#2753) Replaces the thrown SyntaxError with a reported diagnostic (VUE_ROUTER_B0020). The first definePage() call is kept and the extras are ignored, so the Vite server no longer crashes. --- diff --git a/packages/router/src/unplugin/core/definePage.spec.ts b/packages/router/src/unplugin/core/definePage.spec.ts index 22ab4e094..943a5cba1 100644 --- a/packages/router/src/unplugin/core/definePage.spec.ts +++ b/packages/router/src/unplugin/core/definePage.spec.ts @@ -179,6 +179,52 @@ definePage({ ).toHaveBeenWarned() }) + describe('duplicate definePage()', () => { + const duplicateCode = vue` + +` + + it('does not throw and keeps the first call when extracting', async () => { + const result = (await definePageTransform({ + code: duplicateCode, + id: 'src/pages/dup.vue?definePage&vue', + })) as Exclude + + expect(result).toHaveProperty('code') + expect(result?.code).toContain('first') + expect(result?.code).not.toContain('second') + expect('duplicate definePage() call').toHaveBeenWarned() + }) + + it('removes every call from the component and does not throw', async () => { + const result = (await definePageTransform({ + code: duplicateCode, + id: 'src/pages/dup.vue', + })) as Exclude + + expect(result).toHaveProperty('code') + expect(result?.code).not.toContain('definePage') + expect('duplicate definePage() call').toHaveBeenWarned() + }) + + it('extracts info from the first call only', () => { + expect(extractDefinePageInfo(duplicateCode, 'src/pages/dup.vue')).toEqual( + { + name: 'first', + hasRemainingProperties: false, + } + ) + expect('duplicate definePage() call').toHaveBeenWarned() + }) + }) + it('extracts name and path', () => { expect(extractDefinePageInfo(sampleCode, 'src/pages/basic.vue')).toEqual({ name: 'custom', diff --git a/packages/router/src/unplugin/core/definePage.ts b/packages/router/src/unplugin/core/definePage.ts index 361f2f5e5..6cf1d0c76 100644 --- a/packages/router/src/unplugin/core/definePage.ts +++ b/packages/router/src/unplugin/core/definePage.ts @@ -102,7 +102,8 @@ export function definePageTransform({ : // e.g. index.vue that contains a commented `definePage() null } else if (definePageNodes.length > 1) { - throw new SyntaxError(`duplicate definePage() call`) + // ignore the extra calls and keep only the first one instead of crashing + diagnostics.VUE_ROUTER_B0020({ filename: id }) } const definePageNode = definePageNodes[0]! @@ -207,8 +208,10 @@ export function definePageTransform({ const s = new MagicString(code) - // s.removeNode(definePageNode, { offset }) - s.remove(offset + definePageNode.start!, offset + definePageNode.end!) + // remove every definePage() call so duplicates don't leak into the component + for (const node of definePageNodes) { + s.remove(offset + node.start!, offset + node.end!) + } return generateTransform(s, id) } @@ -259,7 +262,8 @@ export function extractDefinePageInfo( if (!definePageNodes.length) { return } else if (definePageNodes.length > 1) { - throw new SyntaxError(`duplicate definePage() call`) + // ignore the extra calls and keep only the first one instead of crashing + diagnostics.VUE_ROUTER_B0020({ filename: id }) } const definePageNode = definePageNodes[0]! diff --git a/packages/router/src/unplugin/diagnostics.ts b/packages/router/src/unplugin/diagnostics.ts index 2460cdb2f..90f883368 100644 --- a/packages/router/src/unplugin/diagnostics.ts +++ b/packages/router/src/unplugin/diagnostics.ts @@ -55,6 +55,11 @@ export const diagnostics = /*#__PURE__*/ defineDiagnostics({ `route alias array must only contain string literals. Found "${p.found}" in file "${p.filename}".`, fix: 'Only use string literals inside the route `alias` array.', }, + VUE_ROUTER_B0020: { + why: (p: { filename: string }) => + `duplicate definePage() call in file "${p.filename}". Only the first one is used, the rest are ignored.`, + fix: 'A file can only have one definePage() call. Merge them into a single definePage() call.', + }, // --- options.ts --- VUE_ROUTER_B0009: {