From 5d6f84a6240d858db2a697bd6eb8e42ba07c6082 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 8 Jun 2024 13:58:09 +0800 Subject: [PATCH] fix(typescript-plugin): TS not working in template when tsconfig missing (#4452) --- packages/component-meta/lib/base.ts | 9 ++++-- packages/language-core/lib/languageModule.ts | 28 +++++-------------- packages/language-server/node.ts | 11 ++++++-- .../tests/utils/createTester.ts | 15 ++++++---- .../language-service/tests/utils/format.ts | 3 +- packages/tsc/index.ts | 9 ++++-- packages/tsc/tests/dts.spec.ts | 13 ++++++--- packages/typescript-plugin/index.ts | 11 ++++++-- 8 files changed, 58 insertions(+), 41 deletions(-) diff --git a/packages/component-meta/lib/base.ts b/packages/component-meta/lib/base.ts index eaf848b7b7..3f68586cf2 100644 --- a/packages/component-meta/lib/base.ts +++ b/packages/component-meta/lib/base.ts @@ -151,9 +151,14 @@ export function baseCreate( const vueLanguagePlugin = vue.createVueLanguagePlugin( ts, id => id, - ts.sys.useCaseSensitiveFileNames, () => projectHost.getProjectVersion?.() ?? '', - () => projectHost.getScriptFileNames(), + fileName => { + const fileMap = new vue.FileMap(ts.sys.useCaseSensitiveFileNames); + for (const vueFileName of projectHost.getScriptFileNames()) { + fileMap.set(vueFileName, undefined); + } + return fileMap.has(fileName); + }, projectHost.getCompilationSettings(), vueCompilerOptions ); diff --git a/packages/language-core/lib/languageModule.ts b/packages/language-core/lib/languageModule.ts index ff46602b0e..755ad002f1 100644 --- a/packages/language-core/lib/languageModule.ts +++ b/packages/language-core/lib/languageModule.ts @@ -51,20 +51,14 @@ function getFileRegistryKey( return JSON.stringify(values); } -export interface _Plugin extends LanguagePlugin { - getCanonicalFileName: (fileName: string) => string; - pluginContext: Parameters[0]; -} - export function createVueLanguagePlugin( ts: typeof import('typescript'), asFileName: (scriptId: T) => string, - useCaseSensitiveFileNames: boolean, getProjectVersion: () => string, - getScriptFileNames: () => string[] | Set, + isRootFile: (fileName: string) => boolean, compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions -): _Plugin { +): LanguagePlugin { const pluginContext: Parameters[0] = { modules: { '@vue/compiler-dom': vueCompilerOptions.target < 3 @@ -83,16 +77,10 @@ export function createVueLanguagePlugin( const vueSfcPlugin = useVueFilePlugin(pluginContext); const vitePressSfcPlugin = useMdFilePlugin(pluginContext); const petiteVueSfcPlugin = useHtmlFilePlugin(pluginContext); - const getCanonicalFileName = useCaseSensitiveFileNames - ? (fileName: string) => fileName - : (fileName: string) => fileName.toLowerCase(); - let canonicalRootFileNames = new Set(); let canonicalRootFileNamesVersion: string | undefined; return { - getCanonicalFileName, - pluginContext, getLanguageId(scriptId) { if (vueCompilerOptions.extensions.some(ext => asFileName(scriptId).endsWith(ext))) { return 'vue'; @@ -107,13 +95,11 @@ export function createVueLanguagePlugin( createVirtualCode(scriptId, languageId, snapshot) { if (languageId === 'vue' || languageId === 'markdown' || languageId === 'html') { const fileName = asFileName(scriptId); - const projectVersion = getProjectVersion(); - if (projectVersion !== canonicalRootFileNamesVersion) { - canonicalRootFileNames = new Set([...getScriptFileNames()].map(getCanonicalFileName)); - canonicalRootFileNamesVersion = projectVersion; - } - if (!pluginContext.globalTypesHolder && canonicalRootFileNames.has(getCanonicalFileName(fileName))) { - pluginContext.globalTypesHolder = fileName; + if (!pluginContext.globalTypesHolder && getProjectVersion() !== canonicalRootFileNamesVersion) { + canonicalRootFileNamesVersion = getProjectVersion(); + if (isRootFile(fileName)) { + pluginContext.globalTypesHolder = fileName; + } } const fileRegistry = getFileRegistry(pluginContext.globalTypesHolder === fileName); const code = fileRegistry.get(fileName); diff --git a/packages/language-server/node.ts b/packages/language-server/node.ts index 11b7071bf4..3181fb4386 100644 --- a/packages/language-server/node.ts +++ b/packages/language-server/node.ts @@ -1,6 +1,6 @@ import type { Connection } from '@volar/language-server'; import { createConnection, createServer, createTypeScriptProject, loadTsdkByPath } from '@volar/language-server/node'; -import { ParsedCommandLine, VueCompilerOptions, createParsedCommandLine, createVueLanguagePlugin, parse, resolveVueCompilerOptions } from '@vue/language-core'; +import { FileMap, ParsedCommandLine, VueCompilerOptions, createParsedCommandLine, createVueLanguagePlugin, parse, resolveVueCompilerOptions } from '@vue/language-core'; import { LanguageServiceEnvironment, convertAttrName, convertTagName, createDefaultGetTsPluginClient, detect, getVueLanguageServicePlugins } from '@vue/language-service'; import * as tsPluginClient from '@vue/typescript-plugin/lib/client'; import { searchNamedPipeServerForFile } from '@vue/typescript-plugin/lib/utils'; @@ -26,9 +26,14 @@ export const getLanguagePlugins: GetLanguagePlugin = async ({ serviceEnv, c const vueLanguagePlugin = createVueLanguagePlugin( tsdk.typescript, asFileName, - sys?.useCaseSensitiveFileNames ?? false, () => projectHost?.getProjectVersion?.() ?? '', - () => projectHost?.getScriptFileNames() ?? [], + fileName => { + const fileMap = new FileMap(sys?.useCaseSensitiveFileNames ?? false); + for (const vueFileName of projectHost?.getScriptFileNames() ?? []) { + fileMap.set(vueFileName, undefined); + } + return fileMap.has(fileName); + }, commandLine?.options ?? {}, vueOptions ); diff --git a/packages/language-service/tests/utils/createTester.ts b/packages/language-service/tests/utils/createTester.ts index 5fa59bfea5..f94d641c54 100644 --- a/packages/language-service/tests/utils/createTester.ts +++ b/packages/language-service/tests/utils/createTester.ts @@ -1,4 +1,4 @@ -import { createLanguage, createLanguageService, createUriMap } from '@volar/language-service'; +import { FileMap, createLanguage, createLanguageService, createUriMap } from '@volar/language-service'; import { TypeScriptProjectHost, createLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript'; import * as path from 'path'; import * as ts from 'typescript'; @@ -27,11 +27,16 @@ function createTester(rootUri: URI) { const vueLanguagePlugin = createVueLanguagePlugin( ts, uriToFileName, - ts.sys.useCaseSensitiveFileNames, () => projectHost.getProjectVersion?.() ?? '', - () => projectHost.getScriptFileNames(), + fileName => { + const fileMap = new FileMap(ts.sys.useCaseSensitiveFileNames); + for (const vueFileName of projectHost.getScriptFileNames()) { + fileMap.set(vueFileName, undefined); + } + return fileMap.has(fileName); + }, parsedCommandLine.options, - parsedCommandLine.vueOptions, + parsedCommandLine.vueOptions ); const vueServicePlugins = getVueLanguageServicePlugins(ts, () => parsedCommandLine.vueOptions); const defaultVSCodeSettings: any = { @@ -60,7 +65,7 @@ function createTester(rootUri: URI) { else { language.scripts.delete(uri); } - }, + } ); language.typescript = { configFileName: realTsConfig, diff --git a/packages/language-service/tests/utils/format.ts b/packages/language-service/tests/utils/format.ts index 8b7f975054..19f479d0a1 100644 --- a/packages/language-service/tests/utils/format.ts +++ b/packages/language-service/tests/utils/format.ts @@ -8,9 +8,8 @@ const resolvedVueOptions = resolveVueCompilerOptions({}); const vueLanguagePlugin = createVueLanguagePlugin( ts, () => '', - false, () => '', - () => [], + () => false, {}, resolvedVueOptions, ); diff --git a/packages/tsc/index.ts b/packages/tsc/index.ts index b9de366f76..4198744b1c 100644 --- a/packages/tsc/index.ts +++ b/packages/tsc/index.ts @@ -32,9 +32,14 @@ export function run() { const vueLanguagePlugin = vue.createVueLanguagePlugin( ts, id => id, - options.host?.useCaseSensitiveFileNames?.() ?? false, () => '', - () => options.rootNames.map(rootName => rootName.replace(windowsPathReg, '/')), + fileName => { + const fileMap = new vue.FileMap(options.host?.useCaseSensitiveFileNames?.() ?? false); + for (const vueFileName of options.rootNames.map(rootName => rootName.replace(windowsPathReg, '/'))) { + fileMap.set(vueFileName, undefined); + } + return fileMap.has(fileName); + }, options.options, vueOptions ); diff --git a/packages/tsc/tests/dts.spec.ts b/packages/tsc/tests/dts.spec.ts index 94896f7dec..efb4fb6f30 100644 --- a/packages/tsc/tests/dts.spec.ts +++ b/packages/tsc/tests/dts.spec.ts @@ -34,11 +34,16 @@ describe('vue-tsc-dts', () => { const vueLanguagePlugin = vue.createVueLanguagePlugin( ts, id => id, - options.host?.useCaseSensitiveFileNames?.() ?? false, () => '', - () => options.rootNames.map(rootName => rootName.replace(windowsPathReg, '/')), + fileName => { + const fileMap = new vue.FileMap(options.host?.useCaseSensitiveFileNames?.() ?? false); + for (const vueFileName of options.rootNames.map(rootName => rootName.replace(windowsPathReg, '/'))) { + fileMap.set(vueFileName, undefined); + } + return fileMap.has(fileName); + }, options.options, - vueOptions, + vueOptions ); return [vueLanguagePlugin]; }); @@ -62,7 +67,7 @@ describe('vue-tsc-dts', () => { outputText = text; }, undefined, - true, + true ); expect(outputText ? normalizeNewline(outputText) : undefined).toMatchSnapshot(); }); diff --git a/packages/typescript-plugin/index.ts b/packages/typescript-plugin/index.ts index d1903b9b77..b8998a7e45 100644 --- a/packages/typescript-plugin/index.ts +++ b/packages/typescript-plugin/index.ts @@ -11,9 +11,16 @@ const plugin = createLanguageServicePlugin( const languagePlugin = vue.createVueLanguagePlugin( ts, id => id, - info.languageServiceHost.useCaseSensitiveFileNames?.() ?? false, () => info.languageServiceHost.getProjectVersion?.() ?? '', - () => externalFiles.get(info.project) ?? [], + info.project.projectKind === ts.server.ProjectKind.Inferred + ? () => true + : fileName => { + const fileMap = new vue.FileMap(info.languageServiceHost.useCaseSensitiveFileNames?.() ?? false); + for (const vueFileName of externalFiles.get(info.project) ?? []) { + fileMap.set(vueFileName, undefined); + } + return fileMap.has(fileName); + }, info.languageServiceHost.getCompilationSettings(), vueOptions );