diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts index 71e4b76cb18..4efd6d4aab9 100644 --- a/packages/compiler-core/src/transforms/transformExpression.ts +++ b/packages/compiler-core/src/transforms/transformExpression.ts @@ -23,14 +23,17 @@ import { makeMap, babelParserDefaultPlugins, hasOwn, - isString + isString, + isReferencedIdentifier, + isInDestructureAssignment, + isStaticProperty, + isStaticPropertyKey, + isFunctionType } from '@vue/shared' import { createCompilerError, ErrorCodes } from '../errors' import { Node, - Function, Identifier, - ObjectProperty, AssignmentExpression, UpdateExpression } from '@babel/types' @@ -279,7 +282,7 @@ export function processExpression( ids.push(node) } } - } else if (isFunction(node)) { + } else if (isFunctionType(node)) { // walk function expressions and add its arguments to known identifiers // so that we don't prefix them node.params.forEach(p => @@ -372,97 +375,16 @@ export function processExpression( return ret } -const isFunction = (node: Node): node is Function => { - return /Function(?:Expression|Declaration)$|Method$/.test(node.type) -} - -const isStaticProperty = (node: Node): node is ObjectProperty => - node && - (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') && - !node.computed - -const isStaticPropertyKey = (node: Node, parent: Node) => - isStaticProperty(parent) && parent.key === node - function shouldPrefix(id: Identifier, parent: Node, parentStack: Node[]) { - // declaration id - if ( - (parent.type === 'VariableDeclarator' || - parent.type === 'ClassDeclaration') && - parent.id === id - ) { - return false - } - - if (isFunction(parent)) { - // function decalration/expression id - if ((parent as any).id === id) { - return false - } - // params list - if (parent.params.includes(id)) { - return false - } - } - - // property key - // this also covers object destructure pattern - if (isStaticPropertyKey(id, parent)) { - return false - } - - // non-assignment array destructure pattern - if ( - parent.type === 'ArrayPattern' && - !isInDestructureAssignment(parent, parentStack) - ) { - return false - } - - // member expression property - if ( - (parent.type === 'MemberExpression' || - parent.type === 'OptionalMemberExpression') && - parent.property === id && - !parent.computed - ) { - return false - } - - // is a special keyword but parsed as identifier - if (id.name === 'arguments') { - return false - } - // skip whitelisted globals if (isGloballyWhitelisted(id.name)) { return false } - // special case for webpack compilation if (id.name === 'require') { return false } - - return true -} - -function isInDestructureAssignment(parent: Node, parentStack: Node[]): boolean { - if ( - parent && - (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern') - ) { - let i = parentStack.length - while (i--) { - const p = parentStack[i] - if (p.type === 'AssignmentExpression') { - return true - } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) { - break - } - } - } - return false + return isReferencedIdentifier(id, parent, parentStack) } function stringifyExpression(exp: ExpressionNode | string): string { diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 5aa8a9f5533..1e560fe8dac 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -22,6 +22,10 @@ import { camelize, capitalize, generateCodeFrame, + isFunctionType, + isReferencedIdentifier, + isStaticProperty, + isStaticPropertyKey, makeMap } from '@vue/shared' import { @@ -32,7 +36,6 @@ import { ArrayPattern, Identifier, ExportSpecifier, - Function as FunctionNode, TSType, TSTypeLiteral, TSFunctionType, @@ -1028,7 +1031,7 @@ export function compileScript( ) { ;(walk as any)(node, { enter(child: Node, parent: Node) { - if (isFunction(child)) { + if (isFunctionType(child)) { this.skip() } if (child.type === 'AwaitExpression') { @@ -1819,11 +1822,11 @@ export function walkIdentifiers( if (node.type === 'Identifier') { if ( !knownIds[node.name] && - isRefIdentifier(node, parent!, parentStack) + isReferencedIdentifier(node, parent!, parentStack) ) { onIdentifier(node, parent!, parentStack) } - } else if (isFunction(node)) { + } else if (isFunctionType(node)) { // #3445 // should not rewrite local variables sharing a name with a top-level ref if (node.body.type === 'BlockStatement') { @@ -1881,79 +1884,6 @@ export function walkIdentifiers( }) } -function isRefIdentifier( - id: Identifier, - parent: Node | null, - parentStack: Node[] -) { - if (!parent) { - return true - } - - // declaration id - if ( - (parent.type === 'VariableDeclarator' || - parent.type === 'ClassDeclaration') && - parent.id === id - ) { - return false - } - - if (isFunction(parent)) { - // function decalration/expression id - if ((parent as any).id === id) { - return false - } - // params list - if (parent.params.includes(id)) { - return false - } - } - - // property key - // this also covers object destructure pattern - if (isStaticPropertyKey(id, parent)) { - return false - } - - // non-assignment array destructure pattern - if ( - parent.type === 'ArrayPattern' && - !isInDestructureAssignment(parent, parentStack) - ) { - return false - } - - // member expression property - if ( - (parent.type === 'MemberExpression' || - parent.type === 'OptionalMemberExpression') && - parent.property === id && - !parent.computed - ) { - return false - } - - // is a special keyword but parsed as identifier - if (id.name === 'arguments') { - return false - } - - return true -} - -const isStaticProperty = (node: Node): node is ObjectProperty => - node && - (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') && - !node.computed - -const isStaticPropertyKey = (node: Node, parent: Node) => - isStaticProperty(parent) && parent.key === node - -function isFunction(node: Node): node is FunctionNode { - return /Function(?:Expression|Declaration)$|Method$/.test(node.type) -} - function isCallOf( node: Node | null | undefined, test: string | ((id: string) => boolean) diff --git a/packages/runtime-core/src/helpers/refSugar.ts b/packages/runtime-core/src/helpers/refSugar.ts index b9d8d7c9b67..45d0c399e2c 100644 --- a/packages/runtime-core/src/helpers/refSugar.ts +++ b/packages/runtime-core/src/helpers/refSugar.ts @@ -8,27 +8,29 @@ import { WritableComputedRef } from '@vue/reactivity' -export function $ref(arg: T | Ref): UnwrapRef +declare const RefMarker: unique symbol +type RefValue = T & { [RefMarker]?: any } + +export function $ref(arg?: T | Ref): RefValue> export function $ref() {} -export function $shallowRef(arg: T): T { - return arg -} +export function $shallowRef(arg?: T): RefValue +export function $shallowRef() {} declare const ComputedRefMarker: unique symbol -type ComputedValue = T & { [ComputedRefMarker]?: any } +type ComputedRefValue = T & { [ComputedRefMarker]?: any } declare const WritableComputedRefMarker: unique symbol -type WritableComputedValue = T & { [WritableComputedRefMarker]?: any } +type WritableComputedRefValue = T & { [WritableComputedRefMarker]?: any } export function $computed( getter: () => T, debuggerOptions?: DebuggerOptions -): ComputedValue +): ComputedRefValue export function $computed( options: WritableComputedOptions, debuggerOptions?: DebuggerOptions -): WritableComputedValue +): WritableComputedRefValue export function $computed() {} export function $fromRefs(source: T): ShallowUnwrapRef @@ -36,9 +38,13 @@ export function $fromRefs() { return null as any } -export function $raw(value: ComputedValue): ComputedRef -export function $raw(value: WritableComputedValue): WritableComputedRef -export function $raw(value: T): Ref +export function $raw>( + value: T +): T extends ComputedRefValue ? ComputedRef : never +export function $raw( + value: WritableComputedRefValue +): WritableComputedRef +export function $raw(value: RefValue): Ref export function $raw() { return null as any } diff --git a/packages/shared/src/astUtils.ts b/packages/shared/src/astUtils.ts new file mode 100644 index 00000000000..cb01c8e9a1e --- /dev/null +++ b/packages/shared/src/astUtils.ts @@ -0,0 +1,72 @@ +import { + Identifier, + Node, + isReferenced, + Function, + ObjectProperty +} from '@babel/types' + +export function isReferencedIdentifier( + id: Identifier, + parent: Node | null, + parentStack: Node[] +) { + if (!parent) { + return true + } + + // is a special keyword but parsed as identifier + if (id.name === 'arguments') { + return false + } + + if (isReferenced(id, parent)) { + return true + } + + // babel's isReferenced check returns false for ids being assigned to, so we + // need to cover those cases here + switch (parent.type) { + case 'AssignmentExpression': + case 'AssignmentPattern': + return true + case 'ObjectPattern': + case 'ArrayPattern': + return isInDestructureAssignment(parent, parentStack) + } + + return false +} + +export function isInDestructureAssignment( + parent: Node, + parentStack: Node[] +): boolean { + if ( + parent && + (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern') + ) { + let i = parentStack.length + while (i--) { + const p = parentStack[i] + if (p.type === 'AssignmentExpression') { + return true + } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) { + break + } + } + } + return false +} + +export const isFunctionType = (node: Node): node is Function => { + return /Function(?:Expression|Declaration)$|Method$/.test(node.type) +} + +export const isStaticProperty = (node: Node): node is ObjectProperty => + node && + (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') && + !node.computed + +export const isStaticPropertyKey = (node: Node, parent: Node) => + isStaticProperty(parent) && parent.key === node diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 8c98b3b6cc4..0bd2df43802 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -12,6 +12,7 @@ export * from './domAttrConfig' export * from './escapeHtml' export * from './looseEqual' export * from './toDisplayString' +export * from './astUtils' /** * List of @babel/parser plugins that are used for template expression