]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): fix type import from path aliased vue file
authorEvan You <yyx990803@gmail.com>
Thu, 18 May 2023 04:50:20 +0000 (12:50 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 18 May 2023 04:50:47 +0000 (12:50 +0800)
close #8348

packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts
packages/compiler-sfc/src/script/context.ts
packages/compiler-sfc/src/script/resolveType.ts

index 3d2387481998fc353f2090c0a2aaf59a710ab762..dd47344277c562b19b68dcaf877f0d4097441682 100644 (file)
@@ -685,6 +685,34 @@ describe('resolveType', () => {
       expect(deps && [...deps]).toStrictEqual(['/user.ts'])
     })
 
+    test('ts module resolve w/ path aliased vue file', () => {
+      const files = {
+        '/tsconfig.json': JSON.stringify({
+          compilerOptions: {
+            include: ['**/*.ts', '**/*.vue'],
+            paths: {
+              '@/*': ['./src/*']
+            }
+          }
+        }),
+        '/src/Foo.vue':
+          '<script lang="ts">export type P = { bar: string }</script>'
+      }
+
+      const { props, deps } = resolve(
+        `
+        import { P } from '@/Foo.vue'
+        defineProps<P>()
+        `,
+        files
+      )
+
+      expect(props).toStrictEqual({
+        bar: ['String']
+      })
+      expect(deps && [...deps]).toStrictEqual(['/src/Foo.vue'])
+    })
+
     test('global types', () => {
       const files = {
         // ambient
index 9a3d2eccd3c986cb239c08a9073bf1681d308379..af2dee16568ce711a5a4429d9a515ce4504bf08c 100644 (file)
@@ -70,6 +70,11 @@ export class ScriptCompileContext {
    */
   deps?: Set<string>
 
+  /**
+   * cache for resolved fs
+   */
+  fs?: NonNullable<SFCScriptCompileOptions['fs']>
+
   constructor(
     public descriptor: SFCDescriptor,
     public options: Partial<SFCScriptCompileOptions>
index 35a80e1b18611c9ab4468fa8708cc5a98f81948b..83d4fbfc887053f402597a6c2062b5e84812b2ea 100644 (file)
@@ -62,7 +62,9 @@ export type SimpleTypeResolveContext = Pick<
   // required
   'source' | 'filename' | 'error' | 'options'
 > &
-  Partial<Pick<ScriptCompileContext, 'scope' | 'globalScopes' | 'deps'>> & {
+  Partial<
+    Pick<ScriptCompileContext, 'scope' | 'globalScopes' | 'deps' | 'fs'>
+  > & {
     ast: Statement[]
   }
 
@@ -693,7 +695,7 @@ function qualifiedNameToPath(node: Identifier | TSQualifiedName): string[] {
 
 function resolveGlobalScope(ctx: TypeResolveContext): TypeScope[] | undefined {
   if (ctx.options.globalTypeFiles) {
-    const fs: FS = ctx.options.fs || ts?.sys
+    const fs = resolveFS(ctx)
     if (!fs) {
       throw new Error('[vue/compiler-sfc] globalTypeFiles requires fs access.')
     }
@@ -714,6 +716,30 @@ export function registerTS(_ts: any) {
 
 type FS = NonNullable<SFCScriptCompileOptions['fs']>
 
+function resolveFS(ctx: TypeResolveContext): FS | undefined {
+  if (ctx.fs) {
+    return ctx.fs
+  }
+  const fs = ctx.options.fs || ts.sys
+  if (!fs) {
+    return
+  }
+  return (ctx.fs = {
+    fileExists(file) {
+      if (file.endsWith('.vue.ts')) {
+        file = file.replace(/\.ts$/, '')
+      }
+      return fs.fileExists(file)
+    },
+    readFile(file) {
+      if (file.endsWith('.vue.ts')) {
+        file = file.replace(/\.ts$/, '')
+      }
+      return fs.readFile(file)
+    }
+  })
+}
+
 function resolveTypeFromImport(
   ctx: TypeResolveContext,
   node: ReferenceTypes,
@@ -731,9 +757,9 @@ function importSourceToScope(
   scope: TypeScope,
   source: string
 ): TypeScope {
-  const fs: FS = ctx.options.fs || ts?.sys
+  const fs = resolveFS(ctx)
   if (!fs) {
-    ctx.error(
+    return ctx.error(
       `No fs option provided to \`compileScript\` in non-Node environment. ` +
         `File system access is required for resolving imported types.`,
       node,
@@ -881,7 +907,11 @@ function resolveWithTS(
   )
 
   if (res.resolvedModule) {
-    return res.resolvedModule.resolvedFileName
+    let filename = res.resolvedModule.resolvedFileName
+    if (filename.endsWith('.vue.ts')) {
+      filename = filename.replace(/\.ts$/, '')
+    }
+    return filename
   }
 }
 
@@ -936,7 +966,7 @@ export function fileToScope(
     return cached
   }
   // fs should be guaranteed to exist here
-  const fs = ctx.options.fs || ts?.sys
+  const fs = resolveFS(ctx)!
   const source = fs.readFile(filename) || ''
   const body = parseFile(filename, source, ctx.options.babelParserPlugins)
   const scope = new TypeScope(filename, source, 0, recordImports(body))