Skip to content

Commit

Permalink
fix(compiler-sfc): check binding is prop before erroring
Browse files Browse the repository at this point in the history
fix #8017
  • Loading branch information
yyx990803 committed Apr 4, 2023
1 parent 9a09e47 commit f3145a9
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -354,5 +354,20 @@ describe('sfc props transform', () => {
)
).toThrow(`Default value of prop "foo" does not match declared type.`)
})

// #8017
test('should not throw an error if the variable is not a props', () => {
expect(() =>
compile(
`<script setup lang='ts'>
import { watch } from 'vue'
const { userId } = defineProps({ userId: Number })
const { error: e, info } = useRequest();
watch(e, () => {});
watch(info, () => {});
</script>`
)
).not.toThrowError()
})
})
})
95 changes: 43 additions & 52 deletions packages/compiler-sfc/src/compileScriptPropsDestructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
isCallOf,
unwrapTSNode
} from '@vue/compiler-core'
import { hasOwn, genPropsAccessExp } from '@vue/shared'
import { genPropsAccessExp } from '@vue/shared'
import { PropsDestructureBindings } from './compileScript'

/**
Expand Down Expand Up @@ -47,6 +47,15 @@ export function transformDestructuredProps(
propsLocalToPublicMap[local] = key
}

function pushScope() {
scopeStack.push((currentScope = Object.create(currentScope)))
}

function popScope() {
scopeStack.pop()
currentScope = scopeStack[scopeStack.length - 1] || null
}

function registerLocalBinding(id: Identifier) {
excludedIds.add(id)
if (currentScope) {
Expand Down Expand Up @@ -108,54 +117,41 @@ export function transformDestructuredProps(
}
}

function rewriteId(
scope: Scope,
id: Identifier,
parent: Node,
parentStack: Node[]
): boolean {
if (hasOwn(scope, id.name)) {
const binding = scope[id.name]

if (binding) {
if (
(parent.type === 'AssignmentExpression' && id === parent.left) ||
parent.type === 'UpdateExpression'
) {
error(`Cannot assign to destructured props as they are readonly.`, id)
}
function rewriteId(id: Identifier, parent: Node, parentStack: Node[]) {
if (
(parent.type === 'AssignmentExpression' && id === parent.left) ||
parent.type === 'UpdateExpression'
) {
error(`Cannot assign to destructured props as they are readonly.`, id)
}

if (isStaticProperty(parent) && parent.shorthand) {
// let binding used in a property shorthand
// skip for destructure patterns
if (
!(parent as any).inPattern ||
isInDestructureAssignment(parent, parentStack)
) {
// { prop } -> { prop: __props.prop }
s.appendLeft(
id.end! + offset,
`: ${genPropsAccessExp(propsLocalToPublicMap[id.name])}`
)
}
} else {
// x --> __props.x
s.overwrite(
id.start! + offset,
id.end! + offset,
genPropsAccessExp(propsLocalToPublicMap[id.name])
)
}
if (isStaticProperty(parent) && parent.shorthand) {
// let binding used in a property shorthand
// skip for destructure patterns
if (
!(parent as any).inPattern ||
isInDestructureAssignment(parent, parentStack)
) {
// { prop } -> { prop: __props.prop }
s.appendLeft(
id.end! + offset,
`: ${genPropsAccessExp(propsLocalToPublicMap[id.name])}`
)
}
return true
} else {
// x --> __props.x
s.overwrite(
id.start! + offset,
id.end! + offset,
genPropsAccessExp(propsLocalToPublicMap[id.name])
)
}
return false
}

function checkUsage(node: Node, method: string, alias = method) {
if (isCallOf(node, alias)) {
const arg = unwrapTSNode(node.arguments[0])
if (arg.type === 'Identifier') {
if (arg.type === 'Identifier' && currentScope[arg.name]) {
error(
`"${arg.name}" is a destructured prop and should not be passed directly to ${method}(). ` +
`Pass a getter () => ${arg.name} instead.`,
Expand Down Expand Up @@ -187,7 +183,7 @@ export function transformDestructuredProps(

// function scopes
if (isFunctionType(node)) {
scopeStack.push((currentScope = {}))
pushScope()
walkFunctionParams(node, registerLocalBinding)
if (node.body.type === 'BlockStatement') {
walkScope(node.body)
Expand All @@ -197,7 +193,7 @@ export function transformDestructuredProps(

// catch param
if (node.type === 'CatchClause') {
scopeStack.push((currentScope = {}))
pushScope()
if (node.param && node.param.type === 'Identifier') {
registerLocalBinding(node.param)
}
Expand All @@ -207,7 +203,7 @@ export function transformDestructuredProps(

// non-function block scopes
if (node.type === 'BlockStatement' && !isFunctionType(parent!)) {
scopeStack.push((currentScope = {}))
pushScope()
walkScope(node)
return
}
Expand All @@ -217,12 +213,8 @@ export function transformDestructuredProps(
isReferencedIdentifier(node, parent!, parentStack) &&
!excludedIds.has(node)
) {
// walk up the scope chain to check if id should be appended .value
let i = scopeStack.length
while (i--) {
if (rewriteId(scopeStack[i], node, parent!, parentStack)) {
return
}
if (currentScope[node.name]) {
rewriteId(node, parent!, parentStack)
}
}
}
Expand All @@ -233,8 +225,7 @@ export function transformDestructuredProps(
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
isFunctionType(node)
) {
scopeStack.pop()
currentScope = scopeStack[scopeStack.length - 1] || null
popScope()
}
}
})
Expand Down

0 comments on commit f3145a9

Please sign in to comment.