]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat(unplugin): don't crash on duplicate definePage() calls (#2753)
authorEduardo San Martin Morote <posva@users.noreply.github.com>
Thu, 16 Jul 2026 15:10:43 +0000 (17:10 +0200)
committerGitHub <noreply@github.com>
Thu, 16 Jul 2026 15:10:43 +0000 (17:10 +0200)
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.

packages/router/src/unplugin/core/definePage.spec.ts
packages/router/src/unplugin/core/definePage.ts
packages/router/src/unplugin/diagnostics.ts

index 22ab4e0945e5e4107799c6a692bec7e8c6c33339..943a5cba14d72b7c0b128c46b5a99ad7bdd8948d 100644 (file)
@@ -179,6 +179,52 @@ definePage({
     ).toHaveBeenWarned()
   })
 
+  describe('duplicate definePage()', () => {
+    const duplicateCode = vue`
+<script setup>
+definePage({
+  name: 'first',
+})
+definePage({
+  name: 'second',
+})
+</script>
+`
+
+    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<TransformResult, string>
+
+      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<TransformResult, string>
+
+      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',
index 361f2f5e578d63f000d08e2a7fb01176affe3494..6cf1d0c760b1aa486fc7ef5646be4f6978162593 100644 (file)
@@ -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]!
index 2460cdb2f79cb68d7503d5360e8054ba97063a99..90f883368afc4e21983563bad8a5f2e815aee038 100644 (file)
@@ -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: {