Skip to content

Commit eb51b23

Browse files
authored
fix(compile-sfc): fix identifier prefixing edge case caused by reused AST (#9867)
close #9853 close #9863
1 parent 1d79b64 commit eb51b23

File tree

2 files changed

+56
-12
lines changed

2 files changed

+56
-12
lines changed

packages/compiler-core/src/babelUtils.ts

+26-12
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,24 @@ export function walkIdentifiers(
5555
// mark property in destructure pattern
5656
;(node as any).inPattern = true
5757
} else if (isFunctionType(node)) {
58-
// walk function expressions and add its arguments to known identifiers
59-
// so that we don't prefix them
60-
walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))
58+
if (node.scopeIds) {
59+
node.scopeIds.forEach(id => markKnownIds(id, knownIds))
60+
} else {
61+
// walk function expressions and add its arguments to known identifiers
62+
// so that we don't prefix them
63+
walkFunctionParams(node, id =>
64+
markScopeIdentifier(node, id, knownIds)
65+
)
66+
}
6167
} else if (node.type === 'BlockStatement') {
62-
// #3445 record block-level local variables
63-
walkBlockDeclarations(node, id =>
64-
markScopeIdentifier(node, id, knownIds)
65-
)
68+
if (node.scopeIds) {
69+
node.scopeIds.forEach(id => markKnownIds(id, knownIds))
70+
} else {
71+
// #3445 record block-level local variables
72+
walkBlockDeclarations(node, id =>
73+
markScopeIdentifier(node, id, knownIds)
74+
)
75+
}
6676
}
6777
},
6878
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
@@ -227,6 +237,14 @@ export function extractIdentifiers(
227237
return nodes
228238
}
229239

240+
function markKnownIds(name: string, knownIds: Record<string, number>) {
241+
if (name in knownIds) {
242+
knownIds[name]++
243+
} else {
244+
knownIds[name] = 1
245+
}
246+
}
247+
230248
function markScopeIdentifier(
231249
node: Node & { scopeIds?: Set<string> },
232250
child: Identifier,
@@ -236,11 +254,7 @@ function markScopeIdentifier(
236254
if (node.scopeIds && node.scopeIds.has(name)) {
237255
return
238256
}
239-
if (name in knownIds) {
240-
knownIds[name]++
241-
} else {
242-
knownIds[name] = 1
243-
}
257+
markKnownIds(name, knownIds)
244258
;(node.scopeIds || (node.scopeIds = new Set())).add(name)
245259
}
246260

packages/compiler-sfc/__tests__/compileTemplate.spec.ts

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
SFCTemplateCompileOptions
55
} from '../src/compileTemplate'
66
import { parse, SFCTemplateBlock } from '../src/parse'
7+
import { compileScript } from '../src'
78

89
function compile(opts: Omit<SFCTemplateCompileOptions, 'id'>) {
910
return compileTemplate({
@@ -397,6 +398,35 @@ test('dynamic v-on + static v-on should merged', () => {
397398
expect(result.code).toMatchSnapshot()
398399
})
399400

401+
// #9853 regression found in Nuxt tests
402+
// walkIdentifiers can get called multiple times on the same node
403+
// due to #9729 calling it during SFC template usage check.
404+
// conditions needed:
405+
// 1. `<script setup lang="ts">`
406+
// 2. Has import
407+
// 3. inlineTemplate: false
408+
// 4. AST being reused
409+
test('prefixing edge case for reused AST', () => {
410+
const src = `
411+
<script setup lang="ts">
412+
import { Foo } from './foo'
413+
</script>
414+
<template>
415+
{{ list.map((t, index) => ({ t: t })) }}
416+
</template>
417+
`
418+
const { descriptor } = parse(src)
419+
// compileScript triggers importUsageCheck
420+
compileScript(descriptor, { id: 'xxx' })
421+
const { code } = compileTemplate({
422+
id: 'xxx',
423+
filename: 'test.vue',
424+
ast: descriptor.template!.ast,
425+
source: descriptor.template!.content
426+
})
427+
expect(code).not.toMatch(`_ctx.t`)
428+
})
429+
400430
interface Pos {
401431
line: number
402432
column: number

0 commit comments

Comments
 (0)