]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): avoid exposing imports not used in template
authorEvan You <yyx990803@gmail.com>
Thu, 22 Jul 2021 16:04:46 +0000 (12:04 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 22 Jul 2021 16:04:46 +0000 (12:04 -0400)
close #3183

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

index fde572c8610fe6108081986e12e62982c7f12811..cc44a7ab10ee4ba7494ffcc8d38d150e85c37ffe 100644 (file)
@@ -204,6 +204,22 @@ return { x }
 }"
 `;
 
+exports[`SFC compile <script setup> imports imports not used in <template> should not be exposed 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { FooBar, FooBaz, FooQux, vMyDir, x, y } from './x'
+        
+export default _defineComponent({
+  setup(__props, { expose }) {
+  expose()
+
+        const fooBar: FooBar = 1
+        
+return { fooBar, FooBaz, FooQux, vMyDir, x }
+}
+
+})"
+`;
+
 exports[`SFC compile <script setup> imports should allow defineProps/Emit at the start of imports 1`] = `
 "import { ref } from 'vue'
       
index 1fc9827cd6e3cf905bd4a483d0c88a8bfe80c0ff..6d18c9d789af6cd28784522e9c60e2bddcd64214 100644 (file)
@@ -209,6 +209,25 @@ defineExpose({ foo: 123 })
         content.lastIndexOf(`import { x }`)
       )
     })
+
+    test('imports not used in <template> should not be exposed', () => {
+      const { content } = compile(`
+        <script setup lang="ts">
+        import { FooBar, FooBaz, FooQux, vMyDir, x, y } from './x'
+        const fooBar: FooBar = 1
+        </script>
+        <template>
+          <FooBaz v-my-dir>{{ x }} {{ yy }}</FooBaz>
+          <foo-qux/>
+        </template>
+        `)
+      assertCode(content)
+      // FooBaz: used as PascalCase component
+      // FooQux: used as kebab-case component
+      // vMyDir: used as directive v-my-dir
+      // x: used in interpolation
+      expect(content).toMatch(`return { fooBar, FooBaz, FooQux, vMyDir, x }`)
+    })
   })
 
   describe('inlineTemplate mode', () => {
index 432d977fa341179f224ef1f9412c4e92e7bee4e6..82b84528d0956863c0cb82638709fd57ef85cb3c 100644 (file)
@@ -12,7 +12,11 @@ import {
   TextRange
 } from './parse'
 import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
-import { babelParserDefaultPlugins, generateCodeFrame } from '@vue/shared'
+import {
+  babelParserDefaultPlugins,
+  generateCodeFrame,
+  hyphenate
+} from '@vue/shared'
 import {
   Node,
   Declaration,
@@ -105,6 +109,7 @@ interface ImportBinding {
   source: string
   rangeNode: Node
   isFromSetup: boolean
+  isUsedInTemplate: boolean
 }
 
 interface VariableBinding {
@@ -312,12 +317,21 @@ export function compileScript(
     if (source === 'vue' && imported) {
       userImportAlias[imported] = local
     }
+
+    let isUsedInTemplate = true
+    if (sfc.template && !sfc.template.src) {
+      isUsedInTemplate = new RegExp(
+        `\\b(?:${local}|${hyphenate(local)})\\b`
+      ).test(sfc.template.content)
+    }
+
     userImports[local] = {
       isType,
       imported: imported || 'default',
       source,
       rangeNode,
-      isFromSetup
+      isFromSetup,
+      isUsedInTemplate
     }
   }
 
@@ -1279,7 +1293,7 @@ export function compileScript(
     // return bindings from setup
     const allBindings: Record<string, any> = { ...setupBindings }
     for (const key in userImports) {
-      if (!userImports[key].isType) {
+      if (!userImports[key].isType && userImports[key].isUsedInTemplate) {
         allBindings[key] = true
       }
     }