Skip to content

Commit 5594643

Browse files
authored
fix(compiler-sfc): fix script setup ref assignment codegen edge case (#4520)
fix #4514
1 parent e6fe751 commit 5594643

File tree

5 files changed

+59
-14
lines changed

5 files changed

+59
-14
lines changed

packages/compiler-core/src/transforms/transformExpression.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ export function processExpression(
9999
// function params
100100
asParams = false,
101101
// v-on handler values may contain multiple statements
102-
asRawStatements = false
102+
asRawStatements = false,
103+
localVars: Record<string, number> = Object.create(context.identifiers)
103104
): ExpressionNode {
104105
if (__BROWSER__) {
105106
if (__DEV__) {
@@ -127,7 +128,7 @@ export function processExpression(
127128
const isDestructureAssignment =
128129
parent && isInDestructureAssignment(parent, parentStack)
129130

130-
if (type === BindingTypes.SETUP_CONST) {
131+
if (type === BindingTypes.SETUP_CONST || localVars[raw]) {
131132
return raw
132133
} else if (type === BindingTypes.SETUP_REF) {
133134
return `${raw}.value`
@@ -149,7 +150,13 @@ export function processExpression(
149150
const { right: rVal, operator } = parent as AssignmentExpression
150151
const rExp = rawExp.slice(rVal.start! - 1, rVal.end! - 1)
151152
const rExpString = stringifyExpression(
152-
processExpression(createSimpleExpression(rExp, false), context)
153+
processExpression(
154+
createSimpleExpression(rExp, false),
155+
context,
156+
false,
157+
false,
158+
knownIds
159+
)
153160
)
154161
return `${context.helperString(IS_REF)}(${raw})${
155162
context.isTS ? ` //@ts-ignore\n` : ``
@@ -190,6 +197,7 @@ export function processExpression(
190197
return `$${type}.${raw}`
191198
}
192199
}
200+
193201
// fallback to ctx
194202
return `_ctx.${raw}`
195203
}
@@ -246,7 +254,6 @@ export function processExpression(
246254
}
247255

248256
type QualifiedId = Identifier & PrefixMeta
249-
250257
const ids: QualifiedId[] = []
251258
const parentStack: Node[] = []
252259
const knownIds: Record<string, number> = Object.create(context.identifiers)

packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap

+23-3
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ return { props, bar }
157157
}"
158158
`;
159159
160-
exports[`SFC compile <script setup> defineProps/defineEmits in multi-variable decalration (full removal) 1`] = `
160+
exports[`SFC compile <script setup> defineProps/defineEmits in multi-variable declaration (full removal) 1`] = `
161161
"export default {
162162
props: ['item'],
163163
emits: ['a'],
@@ -173,7 +173,7 @@ return { props, emit }
173173
}"
174174
`;
175175
176-
exports[`SFC compile <script setup> defineProps/defineEmits in multi-variable decalration 1`] = `
176+
exports[`SFC compile <script setup> defineProps/defineEmits in multi-variable declaration 1`] = `
177177
"export default {
178178
props: ['item'],
179179
emits: ['a'],
@@ -506,7 +506,7 @@ return (_ctx, _push, _parent, _attrs) => {
506506
`;
507507
508508
exports[`SFC compile <script setup> inlineTemplate mode template assignment expression codegen 1`] = `
509-
"import { createElementVNode as _createElementVNode, isRef as _isRef, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
509+
"import { createElementVNode as _createElementVNode, isRef as _isRef, unref as _unref, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
510510
511511
import { ref } from 'vue'
512512
@@ -534,6 +534,26 @@ return (_ctx, _cache) => {
534534
}),
535535
_createElementVNode(\\"div\\", {
536536
onClick: _cache[4] || (_cache[4] = $event => (_isRef(v) ? v.value -= 1 : v -= 1))
537+
}),
538+
_createElementVNode(\\"div\\", {
539+
onClick: _cache[5] || (_cache[5] = () => {
540+
let a = '' + _unref(lett)
541+
_isRef(v) ? v.value = a : v = a
542+
})
543+
}),
544+
_createElementVNode(\\"div\\", {
545+
onClick: _cache[6] || (_cache[6] = () => {
546+
// nested scopes
547+
(()=>{
548+
let x = _ctx.a
549+
(()=>{
550+
let z = x
551+
let z2 = z
552+
})
553+
let lz = _ctx.z
554+
})
555+
_isRef(v) ? v.value = _ctx.a : v = _ctx.a
556+
})
537557
})
538558
], 64 /* STABLE_FRAGMENT */))
539559
}

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

+20-2
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const myEmit = defineEmits(['foo', 'bar'])
9898
emits: ['foo', 'bar'],`)
9999
})
100100

101-
test('defineProps/defineEmits in multi-variable decalration', () => {
101+
test('defineProps/defineEmits in multi-variable declaration', () => {
102102
const { content } = compile(`
103103
<script setup>
104104
const props = defineProps(['item']),
@@ -112,7 +112,7 @@ const myEmit = defineEmits(['foo', 'bar'])
112112
expect(content).toMatch(`emits: ['a'],`)
113113
})
114114

115-
test('defineProps/defineEmits in multi-variable decalration (full removal)', () => {
115+
test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
116116
const { content } = compile(`
117117
<script setup>
118118
const props = defineProps(['item']),
@@ -517,6 +517,22 @@ defineExpose({ foo: 123 })
517517
<div @click="lett = count"/>
518518
<div @click="v += 1"/>
519519
<div @click="v -= 1"/>
520+
<div @click="() => {
521+
let a = '' + lett
522+
v = a
523+
}"/>
524+
<div @click="() => {
525+
// nested scopes
526+
(()=>{
527+
let x = a
528+
(()=>{
529+
let z = x
530+
let z2 = z
531+
})
532+
let lz = z
533+
})
534+
v = a
535+
}"/>
520536
</template>
521537
`,
522538
{ inlineTemplate: true }
@@ -531,6 +547,8 @@ defineExpose({ foo: 123 })
531547
)
532548
expect(content).toMatch(`_isRef(v) ? v.value += 1 : v += 1`)
533549
expect(content).toMatch(`_isRef(v) ? v.value -= 1 : v -= 1`)
550+
expect(content).toMatch(`_isRef(v) ? v.value = a : v = a`)
551+
expect(content).toMatch(`_isRef(v) ? v.value = _ctx.a : v = _ctx.a`)
534552
assertCode(content)
535553
})
536554

packages/compiler-sfc/src/compileScript.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ export function compileScript(
526526
/**
527527
* check defaults. If the default object is an object literal with only
528528
* static properties, we can directly generate more optimzied default
529-
* decalrations. Otherwise we will have to fallback to runtime merging.
529+
* declarations. Otherwise we will have to fallback to runtime merging.
530530
*/
531531
function checkStaticDefaults() {
532532
return (
@@ -895,7 +895,7 @@ export function compileScript(
895895
}
896896
}
897897

898-
// walk decalrations to record declared bindings
898+
// walk declarations to record declared bindings
899899
if (
900900
(node.type === 'VariableDeclaration' ||
901901
node.type === 'FunctionDeclaration' ||

packages/runtime-core/src/apiSetupHelpers.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const warnRuntimeUsage = (method: string) =>
4040
* })
4141
* ```
4242
*
43-
* Equivalent type-based decalration:
43+
* Equivalent type-based declaration:
4444
* ```ts
4545
* // will be compiled into equivalent runtime declarations
4646
* const props = defineProps<{
@@ -79,7 +79,7 @@ export function defineProps() {
7979
* const emit = defineEmits(['change', 'update'])
8080
* ```
8181
*
82-
* Example type-based decalration:
82+
* Example type-based declaration:
8383
* ```ts
8484
* const emit = defineEmits<{
8585
* (event: 'change'): void
@@ -147,7 +147,7 @@ type PropsWithDefaults<Base, Defaults> = Base &
147147

148148
/**
149149
* Vue `<script setup>` compiler macro for providing props default values when
150-
* using type-based `defineProps` decalration.
150+
* using type-based `defineProps` declaration.
151151
*
152152
* Example usage:
153153
* ```ts

0 commit comments

Comments
 (0)