fix(compiler-sfc): simulate `allowArbitraryExtensions` on resolving type (#13301)

close #13295
This commit is contained in:
Teages 2025-05-16 08:38:47 +08:00 committed by GitHub
parent 772b0087cb
commit f7ce5ae666
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 5 deletions

View 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'],
})
})
})
})

View 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)