From d4c0c5e93ce3d434c726cd97a054350088d04c5f Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 14 Dec 2023 18:38:04 +0800 Subject: [PATCH] switch holder when original host file when deleted --- .../language-core/src/generators/script.ts | 2 +- packages/language-core/src/languageModule.ts | 83 +++++++++++++------ packages/language-core/src/plugins.ts | 37 +++++---- packages/language-core/src/plugins/vue-tsx.ts | 28 +++---- packages/language-core/src/types.ts | 2 +- 5 files changed, 96 insertions(+), 56 deletions(-) diff --git a/packages/language-core/src/generators/script.ts b/packages/language-core/src/generators/script.ts index a03b25de72..b35d9c13b1 100644 --- a/packages/language-core/src/generators/script.ts +++ b/packages/language-core/src/generators/script.ts @@ -34,7 +34,7 @@ export function* generate( } | undefined, compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, - globalTypesHolder: string, + globalTypesHolder: string | undefined, getGeneratedLength: () => number, linkedCodeMappings: Mapping[] = [], codegenStack: boolean, diff --git a/packages/language-core/src/languageModule.ts b/packages/language-core/src/languageModule.ts index 7ffb1bbe02..38e0bc4e96 100644 --- a/packages/language-core/src/languageModule.ts +++ b/packages/language-core/src/languageModule.ts @@ -1,6 +1,6 @@ import type { LanguagePlugin } from '@volar/language-core'; import * as path from 'path-browserify'; -import { getDefaultVueLanguagePlugins } from './plugins'; +import { getDefaultVueLanguagePlugins, createPluginContext } from './plugins'; import { VueFile } from './virtualFile/vueFile'; import { VueCompilerOptions, VueLanguagePlugin } from './types'; import type * as ts from 'typescript/lib/tsserverlibrary'; @@ -32,6 +32,25 @@ function getVueFileRegistry(key: string, plugins: VueLanguagePlugin[]) { return fileRegistry; } +function getFileRegistryKey( + compilerOptions: ts.CompilerOptions, + vueCompilerOptions: VueCompilerOptions, + plugins: ReturnType[], + globalTypesHolder: string | undefined, +) { + const values = [ + globalTypesHolder, + ...Object.keys(vueCompilerOptions) + .sort() + .filter(key => key !== 'plugins') + .map(key => [key, vueCompilerOptions[key as keyof VueCompilerOptions]]), + [...new Set(plugins.map(plugin => plugin.requiredCompilerOptions ?? []).flat())] + .sort() + .map(key => [key, compilerOptions[key as keyof ts.CompilerOptions]]), + ]; + return JSON.stringify(values); +} + export function createVueLanguage( ts: typeof import('typescript/lib/tsserverlibrary'), compilerOptions: ts.CompilerOptions = {}, @@ -42,6 +61,14 @@ export function createVueLanguage( const vueCompilerOptions = resolveVueCompilerOptions(_vueCompilerOptions); const allowLanguageIds = new Set(['vue']); + const pluginContext = createPluginContext( + ts, + compilerOptions, + vueCompilerOptions, + codegenStack, + globalTypesHolder, + ); + const plugins = getDefaultVueLanguagePlugins(pluginContext); if (vueCompilerOptions.extensions.includes('.md')) { allowLanguageIds.add('markdown'); @@ -51,7 +78,6 @@ export function createVueLanguage( } let fileRegistry: Map | undefined; - let plugins: ReturnType[] = []; return { createVirtualFile(fileName, languageId, snapshot) { @@ -59,30 +85,12 @@ export function createVueLanguage( if (!fileRegistry) { - globalTypesHolder ??= fileName; - - const keys = [ - globalTypesHolder, - ...Object.keys(vueCompilerOptions) - .sort() - .filter(key => key !== 'plugins') - .map(key => [key, vueCompilerOptions[key as keyof VueCompilerOptions]]), - [...new Set(plugins.map(plugin => plugin.requiredCompilerOptions ?? []).flat())] - .sort() - .map(key => [key, compilerOptions[key as keyof ts.CompilerOptions]]), - ]; + pluginContext.globalTypesHolder ??= fileName; fileRegistry = getVueFileRegistry( - JSON.stringify(keys), + getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins, pluginContext.globalTypesHolder), vueCompilerOptions.plugins, ); - plugins = getDefaultVueLanguagePlugins( - ts, - compilerOptions, - vueCompilerOptions, - codegenStack, - globalTypesHolder, - ); } if (fileRegistry.has(fileName)) { @@ -95,8 +103,35 @@ export function createVueLanguage( return vueFile; } }, - updateVirtualFile(sourceFile, snapshot) { - sourceFile.update(snapshot); + updateVirtualFile(vueFile, snapshot) { + vueFile.update(snapshot); + }, + disposeVirtualFile(vueFile, files) { + fileRegistry?.delete(vueFile.fileName); + if (vueFile.fileName === pluginContext.globalTypesHolder) { + if (fileRegistry?.size) { + for (const [fileName, file] of fileRegistry!) { + pluginContext.globalTypesHolder = fileName; + + fileRegistry = getVueFileRegistry( + getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins, pluginContext.globalTypesHolder), + vueCompilerOptions.plugins, + ); + + files.updateSourceFile( + file.fileName, + file.languageId, + // force dirty + { ...file.snapshot }, + ); + break; + } + } + else { + fileRegistry = undefined; + pluginContext.globalTypesHolder = undefined; + } + } }, typescript: { resolveSourceFileName(tsFileName) { diff --git a/packages/language-core/src/plugins.ts b/packages/language-core/src/plugins.ts index eb822463b9..5959b6bbcd 100644 --- a/packages/language-core/src/plugins.ts +++ b/packages/language-core/src/plugins.ts @@ -12,26 +12,13 @@ import { VueCompilerOptions, VueLanguagePlugin } from './types'; import * as CompilerDOM from '@vue/compiler-dom'; import * as CompilerVue2 from './utils/vue2TemplateCompiler'; -export function getDefaultVueLanguagePlugins( +export function createPluginContext( ts: typeof import('typescript/lib/tsserverlibrary'), compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, codegenStack: boolean, - globalTypesHolder: string, + globalTypesHolder: string | undefined, ) { - - const plugins: VueLanguagePlugin[] = [ - useMdFilePlugin, // .md for VitePress - useHtmlFilePlugin, // .html for PetiteVue - useVueFilePlugin, // .vue and others for Vue - useHtmlTemplatePlugin, - useVueSfcStyles, - useVueSfcCustomBlocks, - useVueSfcScriptsFormat, - useVueSfcTemplate, - useVueTsx, - ...vueCompilerOptions.plugins, - ]; const pluginCtx: Parameters[0] = { modules: { '@vue/compiler-dom': vueCompilerOptions.target < 3 @@ -47,8 +34,26 @@ export function getDefaultVueLanguagePlugins( codegenStack, globalTypesHolder, }; + return pluginCtx; +} + +export function getDefaultVueLanguagePlugins(pluginContext: Parameters[0]) { + + const plugins: VueLanguagePlugin[] = [ + useMdFilePlugin, // .md for VitePress + useHtmlFilePlugin, // .html for PetiteVue + useVueFilePlugin, // .vue and others for Vue + useHtmlTemplatePlugin, + useVueSfcStyles, + useVueSfcCustomBlocks, + useVueSfcScriptsFormat, + useVueSfcTemplate, + useVueTsx, + ...pluginContext.vueCompilerOptions.plugins, + ]; + ; const pluginInstances = plugins - .map(plugin => plugin(pluginCtx)) + .map(plugin => plugin(pluginContext)) .sort((a, b) => { const aOrder = a.order ?? 0; const bOrder = b.order ?? 0; diff --git a/packages/language-core/src/plugins/vue-tsx.ts b/packages/language-core/src/plugins/vue-tsx.ts index c594434e5d..1291312261 100644 --- a/packages/language-core/src/plugins/vue-tsx.ts +++ b/packages/language-core/src/plugins/vue-tsx.ts @@ -123,10 +123,10 @@ export default plugin; function createTsx( fileName: string, _sfc: Sfc, - { vueCompilerOptions, compilerOptions, codegenStack, modules, globalTypesHolder }: Parameters[0], + ctx: Parameters[0], ) { - const ts = modules.typescript; + const ts = ctx.modules.typescript; const lang = computed(() => { return !_sfc.script && !_sfc.scriptSetup ? 'ts' : _sfc.scriptSetup && _sfc.scriptSetup.lang !== 'js' ? _sfc.scriptSetup.lang @@ -140,11 +140,11 @@ function createTsx( ); const scriptSetupRanges = computed(() => _sfc.scriptSetup - ? parseScriptSetupRanges(ts, _sfc.scriptSetup.ast, vueCompilerOptions) + ? parseScriptSetupRanges(ts, _sfc.scriptSetup.ast, ctx.vueCompilerOptions) : undefined ); const shouldGenerateScopedClasses = computed(() => { - const option = vueCompilerOptions.experimentalResolveStyleCssClasses; + const option = ctx.vueCompilerOptions.experimentalResolveStyleCssClasses; return _sfc.styles.some(s => { return option === 'always' || (option === 'scoped' && s.scoped); }); @@ -158,7 +158,7 @@ function createTsx( } for (const style of _sfc.styles) { - const option = vueCompilerOptions.experimentalResolveStyleCssClasses; + const option = ctx.vueCompilerOptions.experimentalResolveStyleCssClasses; if (option === 'always' || (option === 'scoped' && style.scoped)) { for (const className of style.classNames) { classes.add(className.text.substring(1)); @@ -181,15 +181,15 @@ function createTsx( const inlineCssCodegenStacks: string[] = []; const codegen = generateTemplate( ts, - compilerOptions, - vueCompilerOptions, + ctx.compilerOptions, + ctx.vueCompilerOptions, _sfc.template, shouldGenerateScopedClasses(), stylesScopedClasses(), hasScriptSetupSlots(), slotsAssignName(), propsAssignName(), - codegenStack, + ctx.codegenStack, ); let current = codegen.next(); @@ -205,7 +205,7 @@ function createTsx( else if (type === 'inlineCss') { inlineCssCodes.push(code); } - if (codegenStack) { + if (ctx.codegenStack) { if (type === 'ts') { tsCodegenStacks.push(stack); } @@ -254,15 +254,15 @@ function createTsx( hasSlot: _template.hasSlot, tagNames: new Set(_template.tagOffsetsMap.keys()), } : undefined, - compilerOptions, - vueCompilerOptions, - globalTypesHolder, + ctx.compilerOptions, + ctx.vueCompilerOptions, + ctx.globalTypesHolder, () => generatedLength, linkedCodeMappings, - codegenStack, + ctx.codegenStack, )) { codes.push(code); - if (codegenStack) { + if (ctx.codegenStack) { codeStacks.push({ stack, length: 1 }); } generatedLength += typeof code === 'string' diff --git a/packages/language-core/src/types.ts b/packages/language-core/src/types.ts index 04265b405f..cca3f9cfc0 100644 --- a/packages/language-core/src/types.ts +++ b/packages/language-core/src/types.ts @@ -66,7 +66,7 @@ export type VueLanguagePlugin = (ctx: { compilerOptions: ts.CompilerOptions; vueCompilerOptions: VueCompilerOptions; codegenStack: boolean; - globalTypesHolder: string; + globalTypesHolder: string | undefined; }) => { version: 1; name?: string;