]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): simulate `allowArbitraryExtensions` on resolving type (#13301)
authorTeages <Teages@teages.xyz>
Fri, 16 May 2025 00:38:47 +0000 (08:38 +0800)
committerGitHub <noreply@github.com>
Fri, 16 May 2025 00:38:47 +0000 (08:38 +0800)
close #13295

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

index 816d74bc64905a4daf1e7aa30147d261d48de4b4..4ced9b8879a2280c35f1cf328f0db4c98e628ef5 100644 (file)
@@ -1434,6 +1434,29 @@ describe('resolveType', () => {
         colsLg: ['Number'],
       })
     })
+
+    test('allowArbitraryExtensions', () => {
+      const files = {
+        '/foo.d.vue.ts': 'export type Foo = number;',
+        '/foo.vue': '<template><div /></template>',
+        '/bar.d.css.ts': 'export type Bar = string;',
+        '/bar.css': ':root { --color: red; }',
+      }
+
+      const { props } = resolve(
+        `
+        import { Foo } from './foo.vue'
+        import { Bar } from './bar.css'
+        defineProps<{ foo: Foo; bar: Bar }>()
+        `,
+        files,
+      )
+
+      expect(props).toStrictEqual({
+        foo: ['Number'],
+        bar: ['String'],
+      })
+    })
   })
 })
 
index 1f3c4d6837c175a8234209de0a2de6d4174d7ade..3507a783c762e82add6204b9e140b3c75386d4e8 100644 (file)
@@ -860,13 +860,13 @@ function resolveFS(ctx: TypeResolveContext): FS | undefined {
   }
   return (ctx.fs = {
     fileExists(file) {
-      if (file.endsWith('.vue.ts')) {
+      if (file.endsWith('.vue.ts') && !file.endsWith('.d.vue.ts')) {
         file = file.replace(/\.ts$/, '')
       }
       return fs.fileExists(file)
     },
     readFile(file) {
-      if (file.endsWith('.vue.ts')) {
+      if (file.endsWith('.vue.ts') && !file.endsWith('.d.vue.ts')) {
         file = file.replace(/\.ts$/, '')
       }
       return fs.readFile(file)
@@ -1059,7 +1059,7 @@ function resolveWithTS(
 
   if (res.resolvedModule) {
     let filename = res.resolvedModule.resolvedFileName
-    if (filename.endsWith('.vue.ts')) {
+    if (filename.endsWith('.vue.ts') && !filename.endsWith('.d.vue.ts')) {
       filename = filename.replace(/\.ts$/, '')
     }
     return fs.realpath ? fs.realpath(filename) : filename
@@ -1129,7 +1129,7 @@ export function fileToScope(
   // fs should be guaranteed to exist here
   const fs = resolveFS(ctx)!
   const source = fs.readFile(filename) || ''
-  const body = parseFile(filename, source, ctx.options.babelParserPlugins)
+  const body = parseFile(filename, source, fs, ctx.options.babelParserPlugins)
   const scope = new TypeScope(filename, source, 0, recordImports(body))
   recordTypes(ctx, body, scope, asGlobal)
   fileToScopeCache.set(filename, scope)
@@ -1139,6 +1139,7 @@ export function fileToScope(
 function parseFile(
   filename: string,
   content: string,
+  fs: FS,
   parserPlugins?: SFCScriptCompileOptions['babelParserPlugins'],
 ): Statement[] {
   const ext = extname(filename)
@@ -1151,7 +1152,21 @@ function parseFile(
       ),
       sourceType: 'module',
     }).program.body
-  } else if (ext === '.vue') {
+  }
+
+  // simulate `allowArbitraryExtensions` on TypeScript >= 5.0
+  const isUnknownTypeSource = !/\.[cm]?[tj]sx?$/.test(filename)
+  const arbitraryTypeSource = `${filename.slice(0, -ext.length)}.d${ext}.ts`
+  const hasArbitraryTypeDeclaration =
+    isUnknownTypeSource && fs.fileExists(arbitraryTypeSource)
+  if (hasArbitraryTypeDeclaration) {
+    return babelParse(fs.readFile(arbitraryTypeSource)!, {
+      plugins: resolveParserPlugins('ts', parserPlugins, true),
+      sourceType: 'module',
+    }).program.body
+  }
+
+  if (ext === '.vue') {
     const {
       descriptor: { script, scriptSetup },
     } = parse(content)