]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compile-sfc): fix identifier prefixing edge case caused by reused AST (#9867)
authoredison <daiwei521@126.com>
Tue, 19 Dec 2023 10:04:36 +0000 (18:04 +0800)
committerGitHub <noreply@github.com>
Tue, 19 Dec 2023 10:04:36 +0000 (18:04 +0800)
close #9853
close #9863

packages/compiler-core/src/babelUtils.ts
packages/compiler-sfc/__tests__/compileTemplate.spec.ts

index a9c1ebe9c32a65878ad67305643e8b7fdc23bfd7..c00c18b3fed30494a66072eab86d4b5c26d9d420 100644 (file)
@@ -55,14 +55,24 @@ export function walkIdentifiers(
         // mark property in destructure pattern
         ;(node as any).inPattern = true
       } else if (isFunctionType(node)) {
-        // walk function expressions and add its arguments to known identifiers
-        // so that we don't prefix them
-        walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))
+        if (node.scopeIds) {
+          node.scopeIds.forEach(id => markKnownIds(id, knownIds))
+        } else {
+          // walk function expressions and add its arguments to known identifiers
+          // so that we don't prefix them
+          walkFunctionParams(node, id =>
+            markScopeIdentifier(node, id, knownIds)
+          )
+        }
       } else if (node.type === 'BlockStatement') {
-        // #3445 record block-level local variables
-        walkBlockDeclarations(node, id =>
-          markScopeIdentifier(node, id, knownIds)
-        )
+        if (node.scopeIds) {
+          node.scopeIds.forEach(id => markKnownIds(id, knownIds))
+        } else {
+          // #3445 record block-level local variables
+          walkBlockDeclarations(node, id =>
+            markScopeIdentifier(node, id, knownIds)
+          )
+        }
       }
     },
     leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
@@ -227,6 +237,14 @@ export function extractIdentifiers(
   return nodes
 }
 
+function markKnownIds(name: string, knownIds: Record<string, number>) {
+  if (name in knownIds) {
+    knownIds[name]++
+  } else {
+    knownIds[name] = 1
+  }
+}
+
 function markScopeIdentifier(
   node: Node & { scopeIds?: Set<string> },
   child: Identifier,
@@ -236,11 +254,7 @@ function markScopeIdentifier(
   if (node.scopeIds && node.scopeIds.has(name)) {
     return
   }
-  if (name in knownIds) {
-    knownIds[name]++
-  } else {
-    knownIds[name] = 1
-  }
+  markKnownIds(name, knownIds)
   ;(node.scopeIds || (node.scopeIds = new Set())).add(name)
 }
 
index 95e1b7aa81a7b59c9fd2d079933cd0b8bf646e6e..26a8b573f4e8591bc6b5e819f65a3dfda108fc19 100644 (file)
@@ -4,6 +4,7 @@ import {
   SFCTemplateCompileOptions
 } from '../src/compileTemplate'
 import { parse, SFCTemplateBlock } from '../src/parse'
+import { compileScript } from '../src'
 
 function compile(opts: Omit<SFCTemplateCompileOptions, 'id'>) {
   return compileTemplate({
@@ -397,6 +398,35 @@ test('dynamic v-on + static v-on should merged', () => {
   expect(result.code).toMatchSnapshot()
 })
 
+// #9853 regression found in Nuxt tests
+// walkIdentifiers can get called multiple times on the same node
+// due to #9729 calling it during SFC template usage check.
+// conditions needed:
+// 1. `<script setup lang="ts">`
+// 2. Has import
+// 3. inlineTemplate: false
+// 4. AST being reused
+test('prefixing edge case for reused AST', () => {
+  const src = `
+  <script setup lang="ts">
+    import { Foo } from './foo'
+  </script>
+  <template>
+    {{ list.map((t, index) => ({ t: t })) }}
+  </template>
+  `
+  const { descriptor } = parse(src)
+  // compileScript triggers importUsageCheck
+  compileScript(descriptor, { id: 'xxx' })
+  const { code } = compileTemplate({
+    id: 'xxx',
+    filename: 'test.vue',
+    ast: descriptor.template!.ast,
+    source: descriptor.template!.content
+  })
+  expect(code).not.toMatch(`_ctx.t`)
+})
+
 interface Pos {
   line: number
   column: number