fix(compiler-sfc): check binding is prop before erroring

fix #8017
This commit is contained in:
Evan You 2023-04-04 18:10:13 +08:00
parent 9a09e47667
commit f3145a915a
2 changed files with 60 additions and 54 deletions

View File

@ -354,5 +354,20 @@ describe('sfc props transform', () => {
) )
).toThrow(`Default value of prop "foo" does not match declared type.`) ).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()
})
}) })
}) })

View File

@ -17,7 +17,7 @@ import {
isCallOf, isCallOf,
unwrapTSNode unwrapTSNode
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { hasOwn, genPropsAccessExp } from '@vue/shared' import { genPropsAccessExp } from '@vue/shared'
import { PropsDestructureBindings } from './compileScript' import { PropsDestructureBindings } from './compileScript'
/** /**
@ -47,6 +47,15 @@ export function transformDestructuredProps(
propsLocalToPublicMap[local] = key 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) { function registerLocalBinding(id: Identifier) {
excludedIds.add(id) excludedIds.add(id)
if (currentScope) { if (currentScope) {
@ -108,16 +117,7 @@ export function transformDestructuredProps(
} }
} }
function rewriteId( function rewriteId(id: Identifier, parent: Node, parentStack: Node[]) {
scope: Scope,
id: Identifier,
parent: Node,
parentStack: Node[]
): boolean {
if (hasOwn(scope, id.name)) {
const binding = scope[id.name]
if (binding) {
if ( if (
(parent.type === 'AssignmentExpression' && id === parent.left) || (parent.type === 'AssignmentExpression' && id === parent.left) ||
parent.type === 'UpdateExpression' parent.type === 'UpdateExpression'
@ -147,15 +147,11 @@ export function transformDestructuredProps(
) )
} }
} }
return true
}
return false
}
function checkUsage(node: Node, method: string, alias = method) { function checkUsage(node: Node, method: string, alias = method) {
if (isCallOf(node, alias)) { if (isCallOf(node, alias)) {
const arg = unwrapTSNode(node.arguments[0]) const arg = unwrapTSNode(node.arguments[0])
if (arg.type === 'Identifier') { if (arg.type === 'Identifier' && currentScope[arg.name]) {
error( error(
`"${arg.name}" is a destructured prop and should not be passed directly to ${method}(). ` + `"${arg.name}" is a destructured prop and should not be passed directly to ${method}(). ` +
`Pass a getter () => ${arg.name} instead.`, `Pass a getter () => ${arg.name} instead.`,
@ -187,7 +183,7 @@ export function transformDestructuredProps(
// function scopes // function scopes
if (isFunctionType(node)) { if (isFunctionType(node)) {
scopeStack.push((currentScope = {})) pushScope()
walkFunctionParams(node, registerLocalBinding) walkFunctionParams(node, registerLocalBinding)
if (node.body.type === 'BlockStatement') { if (node.body.type === 'BlockStatement') {
walkScope(node.body) walkScope(node.body)
@ -197,7 +193,7 @@ export function transformDestructuredProps(
// catch param // catch param
if (node.type === 'CatchClause') { if (node.type === 'CatchClause') {
scopeStack.push((currentScope = {})) pushScope()
if (node.param && node.param.type === 'Identifier') { if (node.param && node.param.type === 'Identifier') {
registerLocalBinding(node.param) registerLocalBinding(node.param)
} }
@ -207,7 +203,7 @@ export function transformDestructuredProps(
// non-function block scopes // non-function block scopes
if (node.type === 'BlockStatement' && !isFunctionType(parent!)) { if (node.type === 'BlockStatement' && !isFunctionType(parent!)) {
scopeStack.push((currentScope = {})) pushScope()
walkScope(node) walkScope(node)
return return
} }
@ -217,12 +213,8 @@ export function transformDestructuredProps(
isReferencedIdentifier(node, parent!, parentStack) && isReferencedIdentifier(node, parent!, parentStack) &&
!excludedIds.has(node) !excludedIds.has(node)
) { ) {
// walk up the scope chain to check if id should be appended .value if (currentScope[node.name]) {
let i = scopeStack.length rewriteId(node, parent!, parentStack)
while (i--) {
if (rewriteId(scopeStack[i], node, parent!, parentStack)) {
return
}
} }
} }
} }
@ -233,8 +225,7 @@ export function transformDestructuredProps(
(node.type === 'BlockStatement' && !isFunctionType(parent!)) || (node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
isFunctionType(node) isFunctionType(node)
) { ) {
scopeStack.pop() popScope()
currentScope = scopeStack[scopeStack.length - 1] || null
} }
} }
}) })