]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: split import usage check tests into dedicated file
authorEvan You <yyx990803@gmail.com>
Sat, 30 Dec 2023 10:13:15 +0000 (18:13 +0800)
committerEvan You <yyx990803@gmail.com>
Sat, 30 Dec 2023 10:13:35 +0000 (18:13 +0800)
packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/__tests__/compileScript/__snapshots__/importUsageCheck.spec.ts.snap [new file with mode: 0644]
packages/compiler-sfc/__tests__/compileScript/importUsageCheck.spec.ts [new file with mode: 0644]

index a2d24599cfab8c5cdcf6553b3d2528d95035003f..d63e6ec4d40f20e7df76167d1035749df11d6df8 100644 (file)
@@ -639,190 +639,6 @@ return { foo, bar, baz, y, z }
 }"
 `;
 
-exports[`SFC compile <script setup> > dev mode import usage check > TS annotations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { Foo, Bar, Baz, Qux, Fred } from './x'
-        const a = 1
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        function b() {}
-        
-return { a, b, get Baz() { return Baz } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > attribute expressions 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { bar, baz } from './x'
-        const cond = true
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        
-return { cond, get bar() { return bar }, get baz() { return baz } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > components 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { FooBar, FooBaz, FooQux, foo } from './x'
-        const fooBar: FooBar = 1
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        
-return { fooBar, get FooBaz() { return FooBaz }, get FooQux() { return FooQux }, get foo() { return foo } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > directive 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { vMyDir } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        
-return { get vMyDir() { return vMyDir } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > dynamic arguments 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { FooBar, foo, bar, unused, baz } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        
-return { get FooBar() { return FooBar }, get foo() { return foo }, get bar() { return bar }, get baz() { return baz } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > js template string interpolations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { VAR, VAR2, VAR3 } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        
-return { get VAR() { return VAR }, get VAR3() { return VAR3 } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > last tag 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { FooBaz, Last } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-        
-return { get FooBaz() { return FooBaz }, get Last() { return Last } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > property access (whitespace) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { Foo, Bar, Baz } from './foo'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-          
-return { get Foo() { return Foo } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > property access 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { Foo, Bar, Baz } from './foo'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-          
-return { get Foo() { return Foo } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > spread operator 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { Foo, Bar, Baz } from './foo'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-          
-return { get Foo() { return Foo } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > template ref 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { foo, bar, Baz } from './foo'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-          
-return { get foo() { return foo }, get bar() { return bar }, get Baz() { return Baz } }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > vue interpolations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { x, y, z, x$y } from './x'
-      
-export default /*#__PURE__*/_defineComponent({
-  setup(__props, { expose: __expose }) {
-  __expose();
-
-      
-return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }
-}
-
-})"
-`;
-
 exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing imported binding 1`] = `
 "import { bar } from './bar'
         
index f6b667f0e5ef58255fc2558ab9615e694aa09359..b692394f51cee61bae7348118164dccf0c8a9426 100644 (file)
@@ -339,227 +339,6 @@ describe('SFC compile <script setup>', () => {
     })
   })
 
-  // in dev mode, declared bindings are returned as an object from setup()
-  // when using TS, users may import types which should not be returned as
-  // values, so we need to check import usage in the template to determine
-  // what to be returned.
-  describe('dev mode import usage check', () => {
-    test('components', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { FooBar, FooBaz, FooQux, foo } from './x'
-        const fooBar: FooBar = 1
-        </script>
-        <template>
-          <FooBaz></FooBaz>
-          <foo-qux/>
-          <foo/>
-          FooBar
-        </template>
-        `)
-      // FooBar: should not be matched by plain text or incorrect case
-      // FooBaz: used as PascalCase component
-      // FooQux: used as kebab-case component
-      // foo: lowercase component
-      expect(content).toMatch(
-        `return { fooBar, get FooBaz() { return FooBaz }, ` +
-          `get FooQux() { return FooQux }, get foo() { return foo } }`,
-      )
-      assertCode(content)
-    })
-
-    test('directive', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { vMyDir } from './x'
-        </script>
-        <template>
-          <div v-my-dir></div>
-        </template>
-        `)
-      expect(content).toMatch(`return { get vMyDir() { return vMyDir } }`)
-      assertCode(content)
-    })
-
-    test('dynamic arguments', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { FooBar, foo, bar, unused, baz } from './x'
-        </script>
-        <template>
-          <FooBar #[foo.slotName] />
-          <FooBar #unused />
-          <div :[bar.attrName]="15"></div>
-          <div unused="unused"></div>
-          <div #[\`item:\${baz.key}\`]="{ value }"></div>
-        </template>
-        `)
-      expect(content).toMatch(
-        `return { get FooBar() { return FooBar }, get foo() { return foo }, ` +
-          `get bar() { return bar }, get baz() { return baz } }`,
-      )
-      assertCode(content)
-    })
-
-    // https://github.com/vuejs/core/issues/4599
-    test('attribute expressions', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { bar, baz } from './x'
-        const cond = true
-        </script>
-        <template>
-          <div :class="[cond ? '' : bar(), 'default']" :style="baz"></div>
-        </template>
-        `)
-      expect(content).toMatch(
-        `return { cond, get bar() { return bar }, get baz() { return baz } }`,
-      )
-      assertCode(content)
-    })
-
-    test('vue interpolations', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      import { x, y, z, x$y } from './x'
-      </script>
-      <template>
-        <div :id="z + 'y'">{{ x }} {{ yy }} {{ x$y }}</div>
-      </template>
-      `)
-      // x: used in interpolation
-      // y: should not be matched by {{ yy }} or 'y' in binding exps
-      // x$y: #4274 should escape special chars when creating Regex
-      expect(content).toMatch(
-        `return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }`,
-      )
-      assertCode(content)
-    })
-
-    // #4340 interpolations in template strings
-    test('js template string interpolations', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { VAR, VAR2, VAR3 } from './x'
-        </script>
-        <template>
-          {{ \`\${VAR}VAR2\${VAR3}\` }}
-        </template>
-        `)
-      // VAR2 should not be matched
-      expect(content).toMatch(
-        `return { get VAR() { return VAR }, get VAR3() { return VAR3 } }`,
-      )
-      assertCode(content)
-    })
-
-    // edge case: last tag in template
-    test('last tag', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { FooBaz, Last } from './x'
-        </script>
-        <template>
-          <FooBaz></FooBaz>
-          <Last/>
-        </template>
-        `)
-      expect(content).toMatch(
-        `return { get FooBaz() { return FooBaz }, get Last() { return Last } }`,
-      )
-      assertCode(content)
-    })
-
-    test('TS annotations', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { Foo, Bar, Baz, Qux, Fred } from './x'
-        const a = 1
-        function b() {}
-        </script>
-        <template>
-          {{ a as Foo }}
-          {{ b<Bar>() }}
-          {{ Baz }}
-          <Comp v-slot="{ data }: Qux">{{ data }}</Comp>
-          <div v-for="{ z = x as Qux } in list as Fred"/>
-        </template>
-        `)
-      expect(content).toMatch(`return { a, b, get Baz() { return Baz } }`)
-      assertCode(content)
-    })
-
-    // vuejs/vue#12591
-    test('v-on inline statement', () => {
-      // should not error
-      compile(`
-      <script setup lang="ts">
-        import { foo } from './foo'
-      </script>
-      <template>
-        <div @click="$emit('update:a');"></div>
-      </template>
-      `)
-    })
-
-    test('template ref', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-          import { foo, bar, Baz } from './foo'
-        </script>
-        <template>
-          <div ref="foo"></div>
-          <div ref=""></div>
-          <Baz ref="bar" />
-        </template>
-        `)
-      expect(content).toMatch(
-        'return { get foo() { return foo }, get bar() { return bar }, get Baz() { return Baz } }',
-      )
-      assertCode(content)
-    })
-
-    // https://github.com/nuxt/nuxt/issues/22416
-    test('property access', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-          import { Foo, Bar, Baz } from './foo'
-        </script>
-        <template>
-          <div>{{ Foo.Bar.Baz }}</div>
-        </template>
-        `)
-      expect(content).toMatch('return { get Foo() { return Foo } }')
-      assertCode(content)
-    })
-
-    test('spread operator', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-          import { Foo, Bar, Baz } from './foo'
-        </script>
-        <template>
-          <div v-bind="{ ...Foo.Bar.Baz }"></div>
-        </template>
-        `)
-      expect(content).toMatch('return { get Foo() { return Foo } }')
-      assertCode(content)
-    })
-
-    test('property access (whitespace)', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-          import { Foo, Bar, Baz } from './foo'
-        </script>
-        <template>
-          <div>{{ Foo . Bar . Baz }}</div>
-        </template>
-        `)
-      expect(content).toMatch('return { get Foo() { return Foo } }')
-      assertCode(content)
-    })
-  })
-
   describe('inlineTemplate mode', () => {
     test('should work', () => {
       const { content } = compile(
diff --git a/packages/compiler-sfc/__tests__/compileScript/__snapshots__/importUsageCheck.spec.ts.snap b/packages/compiler-sfc/__tests__/compileScript/__snapshots__/importUsageCheck.spec.ts.snap
new file mode 100644 (file)
index 0000000..18946eb
--- /dev/null
@@ -0,0 +1,185 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`TS annotations 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { Foo, Bar, Baz, Qux, Fred } from './x'
+    const a = 1
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    function b() {}
+    
+return { a, b, get Baz() { return Baz } }
+}
+
+})"
+`;
+
+exports[`attribute expressions 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { bar, baz } from './x'
+    const cond = true
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    
+return { cond, get bar() { return bar }, get baz() { return baz } }
+}
+
+})"
+`;
+
+exports[`components 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { FooBar, FooBaz, FooQux, foo } from './x'
+    const fooBar: FooBar = 1
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    
+return { fooBar, get FooBaz() { return FooBaz }, get FooQux() { return FooQux }, get foo() { return foo } }
+}
+
+})"
+`;
+
+exports[`directive 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { vMyDir } from './x'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    
+return { get vMyDir() { return vMyDir } }
+}
+
+})"
+`;
+
+exports[`dynamic arguments 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { FooBar, foo, bar, unused, baz } from './x'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    
+return { get FooBar() { return FooBar }, get foo() { return foo }, get bar() { return bar }, get baz() { return baz } }
+}
+
+})"
+`;
+
+exports[`js template string interpolations 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { VAR, VAR2, VAR3 } from './x'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    
+return { get VAR() { return VAR }, get VAR3() { return VAR3 } }
+}
+
+})"
+`;
+
+exports[`last tag 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { FooBaz, Last } from './x'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+    
+return { get FooBaz() { return FooBaz }, get Last() { return Last } }
+}
+
+})"
+`;
+
+exports[`property access (whitespace) 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { Foo, Bar, Baz } from './foo'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      
+return { get Foo() { return Foo } }
+}
+
+})"
+`;
+
+exports[`property access 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { Foo, Bar, Baz } from './foo'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      
+return { get Foo() { return Foo } }
+}
+
+})"
+`;
+
+exports[`spread operator 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { Foo, Bar, Baz } from './foo'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      
+return { get Foo() { return Foo } }
+}
+
+})"
+`;
+
+exports[`template ref 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { foo, bar, Baz } from './foo'
+    
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      
+return { get foo() { return foo }, get bar() { return bar }, get Baz() { return Baz } }
+}
+
+})"
+`;
+
+exports[`vue interpolations 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+import { x, y, z, x$y } from './x'
+  
+export default /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+  
+return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }
+}
+
+})"
+`;
diff --git a/packages/compiler-sfc/__tests__/compileScript/importUsageCheck.spec.ts b/packages/compiler-sfc/__tests__/compileScript/importUsageCheck.spec.ts
new file mode 100644 (file)
index 0000000..06631b2
--- /dev/null
@@ -0,0 +1,221 @@
+import { assertCode, compileSFCScript as compile } from '../utils'
+
+// in dev mode, declared bindings are returned as an object from setup()
+// when using TS, users may import types which should not be returned as
+// values, so we need to check import usage in the template to determine
+// what to be returned.
+
+test('components', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { FooBar, FooBaz, FooQux, foo } from './x'
+    const fooBar: FooBar = 1
+    </script>
+    <template>
+      <FooBaz></FooBaz>
+      <foo-qux/>
+      <foo/>
+      FooBar
+    </template>
+    `)
+  // FooBar: should not be matched by plain text or incorrect case
+  // FooBaz: used as PascalCase component
+  // FooQux: used as kebab-case component
+  // foo: lowercase component
+  expect(content).toMatch(
+    `return { fooBar, get FooBaz() { return FooBaz }, ` +
+      `get FooQux() { return FooQux }, get foo() { return foo } }`,
+  )
+  assertCode(content)
+})
+
+test('directive', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { vMyDir } from './x'
+    </script>
+    <template>
+      <div v-my-dir></div>
+    </template>
+    `)
+  expect(content).toMatch(`return { get vMyDir() { return vMyDir } }`)
+  assertCode(content)
+})
+
+test('dynamic arguments', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { FooBar, foo, bar, unused, baz } from './x'
+    </script>
+    <template>
+      <FooBar #[foo.slotName] />
+      <FooBar #unused />
+      <div :[bar.attrName]="15"></div>
+      <div unused="unused"></div>
+      <div #[\`item:\${baz.key}\`]="{ value }"></div>
+    </template>
+    `)
+  expect(content).toMatch(
+    `return { get FooBar() { return FooBar }, get foo() { return foo }, ` +
+      `get bar() { return bar }, get baz() { return baz } }`,
+  )
+  assertCode(content)
+})
+
+// https://github.com/vuejs/core/issues/4599
+test('attribute expressions', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { bar, baz } from './x'
+    const cond = true
+    </script>
+    <template>
+      <div :class="[cond ? '' : bar(), 'default']" :style="baz"></div>
+    </template>
+    `)
+  expect(content).toMatch(
+    `return { cond, get bar() { return bar }, get baz() { return baz } }`,
+  )
+  assertCode(content)
+})
+
+test('vue interpolations', () => {
+  const { content } = compile(`
+  <script setup lang="ts">
+  import { x, y, z, x$y } from './x'
+  </script>
+  <template>
+    <div :id="z + 'y'">{{ x }} {{ yy }} {{ x$y }}</div>
+  </template>
+  `)
+  // x: used in interpolation
+  // y: should not be matched by {{ yy }} or 'y' in binding exps
+  // x$y: #4274 should escape special chars when creating Regex
+  expect(content).toMatch(
+    `return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }`,
+  )
+  assertCode(content)
+})
+
+// #4340 interpolations in template strings
+test('js template string interpolations', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { VAR, VAR2, VAR3 } from './x'
+    </script>
+    <template>
+      {{ \`\${VAR}VAR2\${VAR3}\` }}
+    </template>
+    `)
+  // VAR2 should not be matched
+  expect(content).toMatch(
+    `return { get VAR() { return VAR }, get VAR3() { return VAR3 } }`,
+  )
+  assertCode(content)
+})
+
+// edge case: last tag in template
+test('last tag', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { FooBaz, Last } from './x'
+    </script>
+    <template>
+      <FooBaz></FooBaz>
+      <Last/>
+    </template>
+    `)
+  expect(content).toMatch(
+    `return { get FooBaz() { return FooBaz }, get Last() { return Last } }`,
+  )
+  assertCode(content)
+})
+
+test('TS annotations', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+    import { Foo, Bar, Baz, Qux, Fred } from './x'
+    const a = 1
+    function b() {}
+    </script>
+    <template>
+      {{ a as Foo }}
+      {{ b<Bar>() }}
+      {{ Baz }}
+      <Comp v-slot="{ data }: Qux">{{ data }}</Comp>
+      <div v-for="{ z = x as Qux } in list as Fred"/>
+    </template>
+    `)
+  expect(content).toMatch(`return { a, b, get Baz() { return Baz } }`)
+  assertCode(content)
+})
+
+// vuejs/vue#12591
+test('v-on inline statement', () => {
+  // should not error
+  compile(`
+  <script setup lang="ts">
+    import { foo } from './foo'
+  </script>
+  <template>
+    <div @click="$emit('update:a');"></div>
+  </template>
+  `)
+})
+
+test('template ref', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+      import { foo, bar, Baz } from './foo'
+    </script>
+    <template>
+      <div ref="foo"></div>
+      <div ref=""></div>
+      <Baz ref="bar" />
+    </template>
+    `)
+  expect(content).toMatch(
+    'return { get foo() { return foo }, get bar() { return bar }, get Baz() { return Baz } }',
+  )
+  assertCode(content)
+})
+
+// https://github.com/nuxt/nuxt/issues/22416
+test('property access', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+      import { Foo, Bar, Baz } from './foo'
+    </script>
+    <template>
+      <div>{{ Foo.Bar.Baz }}</div>
+    </template>
+    `)
+  expect(content).toMatch('return { get Foo() { return Foo } }')
+  assertCode(content)
+})
+
+test('spread operator', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+      import { Foo, Bar, Baz } from './foo'
+    </script>
+    <template>
+      <div v-bind="{ ...Foo.Bar.Baz }"></div>
+    </template>
+    `)
+  expect(content).toMatch('return { get Foo() { return Foo } }')
+  assertCode(content)
+})
+
+test('property access (whitespace)', () => {
+  const { content } = compile(`
+    <script setup lang="ts">
+      import { Foo, Bar, Baz } from './foo'
+    </script>
+    <template>
+      <div>{{ Foo . Bar . Baz }}</div>
+    </template>
+    `)
+  expect(content).toMatch('return { get Foo() { return Foo } }')
+  assertCode(content)
+})