]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): support resolving type declaration from normal script (#5831)
authoredison <daiwei521@126.com>
Wed, 1 Feb 2023 09:24:50 +0000 (17:24 +0800)
committerGitHub <noreply@github.com>
Wed, 1 Feb 2023 09:24:50 +0000 (04:24 -0500)
fix #5830

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

index 47d401eeea7c7afca0c585684a232cfa104ce283..cbe511f1d07c8e2c8bdc7193475019695030a495 100644 (file)
@@ -1446,6 +1446,24 @@ export default /*#__PURE__*/_defineComponent({
 
       
       
+return { emit }
+}
+
+})"
+`;
+
+exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type from normal script 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+        export interface Emits { (e: 'foo' | 'bar'): void }
+      
+export default /*#__PURE__*/_defineComponent({
+  emits: [\\"foo\\", \\"bar\\"],
+  setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
+  expose();
+
+      
+      
 return { emit }
 }
 
@@ -1764,6 +1782,30 @@ return { props, get defaults() { return defaults } }
 })"
 `;
 
+exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) + normal script 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+        interface Props {
+          a?: string;
+        }
+      
+export default /*#__PURE__*/_defineComponent({
+  props: {
+    a: { type: String, required: false, default: \\"a\\" }
+  },
+  setup(__props: any, { expose }) {
+  expose();
+
+const props = __props as { a: string };
+
+        
+      
+return { props }
+}
+
+})"
+`;
+
 exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) 1`] = `
 "import { defineComponent as _defineComponent } from 'vue'
 
index eed363bc031125787e1b6d27e7b8ac5d832103e2..3ea7632f68b5d027fedad48ac2588d1b905359a4 100644 (file)
@@ -1121,6 +1121,22 @@ const emit = defineEmits(['a', 'b'])
       })
     })
 
+    test('withDefaults (static) + normal script', () => {
+      const { content } = compile(`
+      <script lang="ts">
+        interface Props {
+          a?: string;
+        }
+      </script>
+      <script setup lang="ts">
+        const props = withDefaults(defineProps<Props>(), {
+          a: "a",
+        });
+      </script>
+      `)
+      assertCode(content)
+    })
+    
     // #7111
     test('withDefaults (static) w/ production mode', () => {
       const { content } = compile(
@@ -1261,6 +1277,21 @@ const emit = defineEmits(['a', 'b'])
       expect(content).toMatch(`emits: ["foo", "bar"]`)
     })
 
+    
+    test('defineEmits w/ type from normal script', () => {
+      const { content } = compile(`
+      <script lang="ts">
+        export interface Emits { (e: 'foo' | 'bar'): void }
+      </script>
+      <script setup lang="ts">
+      const emit = defineEmits<Emits>()
+      </script>
+      `)
+      assertCode(content)
+      expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
+      expect(content).toMatch(`emits: ["foo", "bar"]`)
+    })
+
     test('defineEmits w/ type (type alias)', () => {
       const { content } = compile(`
       <script setup lang="ts">
index 74cbb5396ae1be891e1071e53450464df76f67ec..322f89b0805451cfe11d4230a7c7254fe768359c 100644 (file)
@@ -136,6 +136,12 @@ export interface ImportBinding {
   isUsedInTemplate: boolean
 }
 
+type FromNormalScript<T> = T & { __fromNormalScript?: boolean | null }
+type PropsDeclType = FromNormalScript<TSTypeLiteral | TSInterfaceBody>
+type EmitsDeclType = FromNormalScript<
+  TSFunctionType | TSTypeLiteral | TSInterfaceBody
+>
+
 /**
  * Compile `<script setup>`
  * It requires the whole SFC descriptor because we need to handle and merge
@@ -287,15 +293,11 @@ export function compileScript(
   let propsRuntimeDefaults: ObjectExpression | undefined
   let propsDestructureDecl: Node | undefined
   let propsDestructureRestId: string | undefined
-  let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
+  let propsTypeDecl: PropsDeclType | undefined
   let propsTypeDeclRaw: Node | undefined
   let propsIdentifier: string | undefined
   let emitsRuntimeDecl: Node | undefined
-  let emitsTypeDecl:
-    | TSFunctionType
-    | TSTypeLiteral
-    | TSInterfaceBody
-    | undefined
+  let emitsTypeDecl: EmitsDeclType | undefined
   let emitsTypeDeclRaw: Node | undefined
   let emitIdentifier: string | undefined
   let hasAwait = false
@@ -436,7 +438,7 @@ export function compileScript(
       propsTypeDecl = resolveQualifiedType(
         propsTypeDeclRaw,
         node => node.type === 'TSTypeLiteral'
-      ) as TSTypeLiteral | TSInterfaceBody | undefined
+      ) as PropsDeclType | undefined
 
       if (!propsTypeDecl) {
         error(
@@ -567,7 +569,7 @@ export function compileScript(
       emitsTypeDecl = resolveQualifiedType(
         emitsTypeDeclRaw,
         node => node.type === 'TSFunctionType' || node.type === 'TSTypeLiteral'
-      ) as TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined
+      ) as EmitsDeclType | undefined
 
       if (!emitsTypeDecl) {
         error(
@@ -667,7 +669,7 @@ export function compileScript(
   function resolveQualifiedType(
     node: Node,
     qualifier: (node: Node) => boolean
-  ) {
+  ): Node | undefined {
     if (qualifier(node)) {
       return node
     }
@@ -677,7 +679,8 @@ export function compileScript(
     ) {
       const refName = node.typeName.name
       const body = getAstBody()
-      for (const node of body) {
+      for (let i = 0; i < body.length; i++) {
+        const node = body[i]
         let qualified = isQualifiedType(
           node,
           qualifier,
@@ -690,6 +693,8 @@ export function compileScript(
             filterExtendsType(extendsTypes, bodies)
             qualified.body = bodies
           }
+          ;(qualified as FromNormalScript<Node>).__fromNormalScript =
+            scriptAst && i >= scriptSetupAst.body.length
           return qualified
         }
       }
@@ -877,8 +882,10 @@ export function compileScript(
     }
   }
 
-  function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
-    const scriptSetupSource = scriptSetup!.content
+  function genSetupPropsType(node: PropsDeclType) {
+    const scriptSource = node.__fromNormalScript
+      ? script!.content
+      : scriptSetup!.content
     if (hasStaticWithDefaults()) {
       // if withDefaults() is used, we need to remove the optional flags
       // on props that have default values
@@ -903,20 +910,19 @@ export function compileScript(
             res +=
               m.key.name +
               (m.type === 'TSMethodSignature' ? '()' : '') +
-              scriptSetupSource.slice(
+              scriptSource.slice(
                 m.typeAnnotation.start!,
                 m.typeAnnotation.end!
               ) +
               ', '
           } else {
-            res +=
-              scriptSetupSource.slice(m.start!, m.typeAnnotation.end!) + `, `
+            res += scriptSource.slice(m.start!, m.typeAnnotation.end!) + `, `
           }
         }
       }
       return (res.length ? res.slice(0, -2) : res) + ` }`
     } else {
-      return scriptSetupSource.slice(node.start!, node.end!)
+      return scriptSource.slice(node.start!, node.end!)
     }
   }
 
@@ -1480,7 +1486,10 @@ export function compileScript(
   if (destructureElements.length) {
     args += `, { ${destructureElements.join(', ')} }`
     if (emitsTypeDecl) {
-      args += `: { emit: (${scriptSetup.content.slice(
+      const content = emitsTypeDecl.__fromNormalScript
+        ? script!.content
+        : scriptSetup.content
+      args += `: { emit: (${content.slice(
         emitsTypeDecl.start!,
         emitsTypeDecl.end!
       )}), expose: any, slots: any, attrs: any }`