]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): check import source during binding analysation (#6826)
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Tue, 8 Nov 2022 09:07:28 +0000 (17:07 +0800)
committerGitHub <noreply@github.com>
Tue, 8 Nov 2022 09:07:28 +0000 (04:07 -0500)
fix #6825

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

index 08602d3384648c2f665cd6242c82698687f15bee..5c03ac17fdbdb2ded562ac26931db2b1251291c9 100644 (file)
@@ -356,6 +356,42 @@ defineExpose({ foo: 123 })
         content.lastIndexOf(`import { x }`)
       )
     })
+
+    describe('import ref/reactive function from other place', () => {
+      test('import directly', () => {
+        const { bindings } = compile(`
+        <script setup>
+          import { ref, reactive } from './foo'
+
+          const foo = ref(1)
+          const bar = reactive(1)
+        </script>
+      `)
+        expect(bindings).toStrictEqual({
+          ref: BindingTypes.SETUP_MAYBE_REF,
+          reactive: BindingTypes.SETUP_MAYBE_REF,
+          foo: BindingTypes.SETUP_MAYBE_REF,
+          bar: BindingTypes.SETUP_MAYBE_REF
+        })
+      })
+
+      test('import w/ alias', () => {
+        const { bindings } = compile(`
+        <script setup>
+          import { ref as _ref, reactive as _reactive } from './foo'
+
+          const foo = ref(1)
+          const bar = reactive(1)
+        </script>
+      `)
+        expect(bindings).toStrictEqual({
+          _reactive: BindingTypes.SETUP_MAYBE_REF,
+          _ref: BindingTypes.SETUP_MAYBE_REF,
+          foo: BindingTypes.SETUP_REF,
+          bar: BindingTypes.SETUP_REACTIVE_CONST
+        })
+      })
+    })
   })
 
   // in dev mode, declared bindings are returned as an object from setup()
index 09222396fc89cb7187cd313ee1e356fabdbb4b49..adc22ab6820ce35cd96c17aa0ba812e7bbf7f8a3 100644 (file)
@@ -129,6 +129,7 @@ export interface SFCScriptCompileOptions {
 export interface ImportBinding {
   isType: boolean
   imported: string
+  local: string
   source: string
   isFromSetup: boolean
   isUsedInTemplate: boolean
@@ -272,7 +273,6 @@ export function compileScript(
   const bindingMetadata: BindingMetadata = {}
   const helperImports: Set<string> = new Set()
   const userImports: Record<string, ImportBinding> = Object.create(null)
-  const userImportAlias: Record<string, string> = Object.create(null)
   const scriptBindings: Record<string, BindingTypes> = Object.create(null)
   const setupBindings: Record<string, BindingTypes> = Object.create(null)
 
@@ -362,10 +362,6 @@ export function compileScript(
     isFromSetup: boolean,
     needTemplateUsageCheck: boolean
   ) {
-    if (source === 'vue' && imported) {
-      userImportAlias[imported] = local
-    }
-
     // template usage check is only needed in non-inline mode, so we can skip
     // the work if inlineTemplate is true.
     let isUsedInTemplate = needTemplateUsageCheck
@@ -382,6 +378,7 @@ export function compileScript(
     userImports[local] = {
       isType,
       imported: imported || 'default',
+      local,
       source,
       isFromSetup,
       isUsedInTemplate
@@ -990,7 +987,7 @@ export function compileScript(
           }
         }
         if (node.declaration) {
-          walkDeclaration(node.declaration, scriptBindings, userImportAlias)
+          walkDeclaration(node.declaration, scriptBindings, userImports)
         }
       } else if (
         (node.type === 'VariableDeclaration' ||
@@ -999,7 +996,7 @@ export function compileScript(
           node.type === 'TSEnumDeclaration') &&
         !node.declare
       ) {
-        walkDeclaration(node, scriptBindings, userImportAlias)
+        walkDeclaration(node, scriptBindings, userImports)
       }
     }
 
@@ -1199,7 +1196,7 @@ export function compileScript(
         node.type === 'ClassDeclaration') &&
       !node.declare
     ) {
-      walkDeclaration(node, setupBindings, userImportAlias)
+      walkDeclaration(node, setupBindings, userImports)
     }
 
     // walk statements & named exports / variable declarations for top level
@@ -1654,8 +1651,17 @@ function registerBinding(
 function walkDeclaration(
   node: Declaration,
   bindings: Record<string, BindingTypes>,
-  userImportAlias: Record<string, string>
+  userImports: Record<string, ImportBinding>
 ) {
+  function getUserBinding(name: string) {
+    const binding = Object.values(userImports).find(
+      binding => binding.source === 'vue' && binding.imported === name
+    )
+    if (binding) return binding.local
+    else if (!userImports[name]) return name
+    return undefined
+  }
+
   if (node.type === 'VariableDeclaration') {
     const isConst = node.kind === 'const'
     // export const foo = ...
@@ -1669,7 +1675,7 @@ function walkDeclaration(
       )
       if (id.type === 'Identifier') {
         let bindingType
-        const userReactiveBinding = userImportAlias['reactive'] || 'reactive'
+        const userReactiveBinding = getUserBinding('reactive')
         if (isCallOf(init, userReactiveBinding)) {
           // treat reactive() calls as let since it's meant to be mutable
           bindingType = isConst
@@ -1685,7 +1691,7 @@ function walkDeclaration(
             ? BindingTypes.SETUP_REACTIVE_CONST
             : BindingTypes.SETUP_CONST
         } else if (isConst) {
-          if (isCallOf(init, userImportAlias['ref'] || 'ref')) {
+          if (isCallOf(init, getUserBinding('ref'))) {
             bindingType = BindingTypes.SETUP_REF
           } else {
             bindingType = BindingTypes.SETUP_MAYBE_REF
@@ -1982,10 +1988,11 @@ function genRuntimeEmits(emits: Set<string>) {
 
 function isCallOf(
   node: Node | null | undefined,
-  test: string | ((id: string) => boolean)
+  test: string | ((id: string) => boolean) | null | undefined
 ): node is CallExpression {
   return !!(
     node &&
+    test &&
     node.type === 'CallExpression' &&
     node.callee.type === 'Identifier' &&
     (typeof test === 'string'
@@ -1994,7 +2001,7 @@ function isCallOf(
   )
 }
 
-function canNeverBeRef(node: Node, userReactiveImport: string): boolean {
+function canNeverBeRef(node: Node, userReactiveImport?: string): boolean {
   if (isCallOf(node, userReactiveImport)) {
     return true
   }