From 8990ddcbb8e1f078eafaba5c786274335f09a163 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 31 Dec 2022 05:41:58 +0800 Subject: [PATCH] feat: Support for Additional Language Modules (Experimental) (#2267) --- .../src/index.ts | 4 ++-- .../language-server/src/common/project.ts | 6 +++--- .../src/common/syntaxServicesHost.ts | 5 +++-- .../src/common/utils/serverConfig.ts | 12 +++++++----- .../language-server/src/common/workspace.ts | 8 ++++---- .../vue-component-meta/src/index.ts | 4 ++-- .../schemas/vue-tsconfig.schema.json | 8 ++++++-- .../vue-language-core/src/languageModule.ts | 9 ++++++--- .../vue-language-core/src/types.ts | 1 + .../vue-language-core/src/utils/ts.ts | 19 +++++++++++++++++++ .../src/languageServerPlugin.ts | 4 ++-- .../src/documentService.ts | 4 ++-- .../src/languageService.ts | 4 ++-- vue-language-tools/vue-tsc/src/index.ts | 6 +++--- .../vue-typescript/src/index.ts | 5 ++--- 15 files changed, 64 insertions(+), 35 deletions(-) diff --git a/examples/vue-and-svelte-language-server/src/index.ts b/examples/vue-and-svelte-language-server/src/index.ts index 914d05af32..3ec0634c58 100644 --- a/examples/vue-and-svelte-language-server/src/index.ts +++ b/examples/vue-and-svelte-language-server/src/index.ts @@ -22,14 +22,14 @@ const plugin: LanguageServerPlugin; @@ -73,7 +73,7 @@ export async function createProject(context: ProjectContext) { context: languageContext, getPlugins() { return [ - ...context.workspacePlugins, + ...context.serverConfig?.plugins ?? [], ...context.workspace.workspaces.plugins.map(plugin => plugin.semanticService?.getServicePlugins?.(languageServiceHost, languageService!) ?? []).flat(), ]; }, diff --git a/packages/language-server/src/common/syntaxServicesHost.ts b/packages/language-server/src/common/syntaxServicesHost.ts index 5deb74648f..64ae4116f3 100644 --- a/packages/language-server/src/common/syntaxServicesHost.ts +++ b/packages/language-server/src/common/syntaxServicesHost.ts @@ -1,7 +1,7 @@ import * as embedded from '@volar/language-service'; import { URI } from 'vscode-uri'; import { LanguageServerInitializationOptions, LanguageServerPlugin, RuntimeEnvironment } from '../types'; -import { loadCustomPlugins } from './utils/serverConfig'; +import { loadServerConfig } from './utils/serverConfig'; // fix build import type * as _ from 'vscode-languageserver-textdocument'; @@ -46,6 +46,7 @@ export function createSyntaxServicesHost( configurationHost: configHost, fileSystemProvider: runtimeEnv.fileSystemProvide, }; + const serverConfig = loadServerConfig(rootUri.fsPath, initOptions.configFilePath); const serviceContext = embedded.createDocumentServiceContext({ ts, env, @@ -54,7 +55,7 @@ export function createSyntaxServicesHost( }, getPlugins() { return [ - ...loadCustomPlugins(rootUri.fsPath, initOptions.configFilePath), + ...serverConfig?.plugins ?? [], ...plugins.map(plugin => plugin.syntacticService?.getServicePlugins?.(serviceContext) ?? []).flat(), ]; }, diff --git a/packages/language-server/src/common/utils/serverConfig.ts b/packages/language-server/src/common/utils/serverConfig.ts index 45b35f1a0a..ab6f33079f 100644 --- a/packages/language-server/src/common/utils/serverConfig.ts +++ b/packages/language-server/src/common/utils/serverConfig.ts @@ -1,6 +1,10 @@ import { LanguageServicePlugin } from '@volar/language-service'; -export function loadCustomPlugins(dir: string, configFile: string | undefined) { +export interface ServerConfig { + plugins?: LanguageServicePlugin[]; +} + +export function loadServerConfig(dir: string, configFile: string | undefined): ServerConfig | undefined { let configPath: string | undefined; try { configPath = require.resolve(configFile ?? './volar.config.js', { paths: [dir] }); @@ -8,14 +12,12 @@ export function loadCustomPlugins(dir: string, configFile: string | undefined) { try { if (configPath) { - const config: { plugins?: LanguageServicePlugin[]; } = require(configPath); + const config: ServerConfig = require(configPath); delete require.cache[configPath]; - return config.plugins ?? []; + return config; } } catch (err) { console.log(err); } - - return []; } diff --git a/packages/language-server/src/common/workspace.ts b/packages/language-server/src/common/workspace.ts index 8535ac2a19..f2f3a0935a 100644 --- a/packages/language-server/src/common/workspace.ts +++ b/packages/language-server/src/common/workspace.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode-languageserver'; import { URI } from 'vscode-uri'; import { createProject, Project } from './project'; import { getInferredCompilerOptions } from './utils/inferredCompilerOptions'; -import { loadCustomPlugins } from './utils/serverConfig'; +import { loadServerConfig } from './utils/serverConfig'; import { createUriMap } from './utils/uriMap'; import { WorkspacesContext } from './workspaces'; @@ -20,7 +20,7 @@ export async function createWorkspace(context: WorkspaceContext) { let inferredProject: Project | undefined; - const workspacePlugins = loadCustomPlugins(shared.getPathOfUri(context.rootUri.toString()), context.workspaces.initOptions.configFilePath); + const serverConfig = loadServerConfig(shared.getPathOfUri(context.rootUri.toString()), context.workspaces.initOptions.configFilePath); const sys = context.workspaces.fileSystemHost.getWorkspaceFileSystem(context.rootUri); const documentRegistry = context.workspaces.ts.createDocumentRegistry(sys.useCaseSensitiveFileNames, shared.getPathOfUri(context.rootUri.toString())); const projects = createUriMap(); @@ -86,10 +86,10 @@ export async function createWorkspace(context: WorkspaceContext) { const inferOptions = await getInferredCompilerOptions(context.workspaces.ts, context.workspaces.configurationHost); return createProject({ workspace: context, - workspacePlugins, rootUri: context.rootUri, tsConfig: inferOptions, documentRegistry, + serverConfig, }); })(); } @@ -218,7 +218,7 @@ export async function createWorkspace(context: WorkspaceContext) { if (!project) { project = createProject({ workspace: context, - workspacePlugins, + serverConfig, rootUri: URI.parse(shared.getUriByPath(path.dirname(tsConfig))), tsConfig, documentRegistry, diff --git a/vue-language-tools/vue-component-meta/src/index.ts b/vue-language-tools/vue-component-meta/src/index.ts index 59e54033cb..0804c5100f 100644 --- a/vue-language-tools/vue-component-meta/src/index.ts +++ b/vue-language-tools/vue-component-meta/src/index.ts @@ -168,13 +168,13 @@ export function baseCreate( return _host[prop as keyof typeof _host]; }, }) as vue.LanguageServiceHost; - const vueLanguageModule = vue.createLanguageModule( + const vueLanguageModules = vue.createLanguageModules( host.getTypeScriptModule(), host.getCurrentDirectory(), host.getCompilationSettings(), host.getVueCompilationSettings(), ); - const core = embedded.createLanguageContext(host, [vueLanguageModule]); + const core = embedded.createLanguageContext(host, vueLanguageModules); const proxyApis: Partial = checkerOptions.forceUseTs ? { getScriptKind: (fileName) => { if (fileName.endsWith('.vue.js')) { diff --git a/vue-language-tools/vue-language-core/schemas/vue-tsconfig.schema.json b/vue-language-tools/vue-language-core/schemas/vue-tsconfig.schema.json index fcda48dbc9..e33611e983 100644 --- a/vue-language-tools/vue-language-core/schemas/vue-tsconfig.schema.json +++ b/vue-language-tools/vue-language-core/schemas/vue-tsconfig.schema.json @@ -60,6 +60,10 @@ "default": [ ], "markdownDescription": "Plugins to be used in the SFC compiler." }, + "hooks": { + "type": "array", + "markdownDescription": "https://github.com/johnsoncodehk/volar/pull/2217" + }, "optionsWrapper": { "type": "array", "default": [ @@ -107,9 +111,9 @@ }, "markdownDescription": "https://github.com/johnsoncodehk/volar/issues/1969" }, - "hooks": { + "experimentalAdditionalLanguageModules": { "type": "array", - "markdownDescription": "https://github.com/johnsoncodehk/volar/pull/2217" + "markdownDescription": "https://github.com/johnsoncodehk/volar/pull/2267" } } } diff --git a/vue-language-tools/vue-language-core/src/languageModule.ts b/vue-language-tools/vue-language-core/src/languageModule.ts index 8508634df7..dfc03f354b 100644 --- a/vue-language-tools/vue-language-core/src/languageModule.ts +++ b/vue-language-tools/vue-language-core/src/languageModule.ts @@ -7,13 +7,13 @@ import * as localTypes from './utils/localTypes'; import { resolveVueCompilerOptions } from './utils/ts'; import type * as ts from 'typescript/lib/tsserverlibrary'; -export function createLanguageModule( +export function createLanguageModules( ts: typeof import('typescript/lib/tsserverlibrary'), rootDir: string, compilerOptions: ts.CompilerOptions, _vueCompilerOptions: VueCompilerOptions, extraPlugins: VueLanguagePlugin[] = [], -): embedded.LanguageModule { +): embedded.LanguageModule[] { const vueCompilerOptions = resolveVueCompilerOptions(_vueCompilerOptions); const vueLanguagePlugin = getDefaultVueLanguagePlugins( @@ -97,7 +97,10 @@ export function createLanguageModule( }, }; - return languageModule; + return [ + languageModule, + ...vueCompilerOptions.experimentalAdditionalLanguageModules?.map(module => require(module)) ?? [], + ]; function getSharedTypesFiles(fileNames: string[]) { const moduleFiles = fileNames.filter(fileName => vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext))); diff --git a/vue-language-tools/vue-language-core/src/types.ts b/vue-language-tools/vue-language-core/src/types.ts index c28718e11a..7d03f3cff2 100644 --- a/vue-language-tools/vue-language-core/src/types.ts +++ b/vue-language-tools/vue-language-core/src/types.ts @@ -32,6 +32,7 @@ export interface ResolvedVueCompilerOptions { experimentalRfc436: boolean; experimentalModelPropName: Record | Record[]>>; experimentalUseElementAccessInTemplate: boolean; + experimentalAdditionalLanguageModules: string[]; } export type VueLanguagePlugin = (ctx: { diff --git a/vue-language-tools/vue-language-core/src/utils/ts.ts b/vue-language-tools/vue-language-core/src/utils/ts.ts index 67d7620e39..e91e2b1767 100644 --- a/vue-language-tools/vue-language-core/src/utils/ts.ts +++ b/vue-language-tools/vue-language-core/src/utils/ts.ts @@ -79,6 +79,15 @@ function createParsedCommandLineBase( ...content.raw.vueCompilerOptions, }; + vueOptions.plugins = vueOptions.plugins?.map(plugin => { + try { + plugin = require.resolve(plugin, { paths: [folder] }); + } + catch (error) { + console.error(error); + } + return plugin; + }); vueOptions.hooks = vueOptions.hooks?.map(hook => { try { hook = require.resolve(hook, { paths: [folder] }); @@ -88,6 +97,15 @@ function createParsedCommandLineBase( } return hook; }); + vueOptions.experimentalAdditionalLanguageModules = vueOptions.experimentalAdditionalLanguageModules?.map(module => { + try { + module = require.resolve(module, { paths: [folder] }); + } + catch (error) { + console.error(error); + } + return module; + }); return { ...content, @@ -148,6 +166,7 @@ export function resolveVueCompilerOptions(vueOptions: VueCompilerOptions): Resol narrowingTypesInInlineHandlers: vueOptions.narrowingTypesInInlineHandlers ?? false, plugins: vueOptions.plugins ?? [], hooks: vueOptions.hooks ?? [], + experimentalAdditionalLanguageModules: vueOptions.experimentalAdditionalLanguageModules ?? [], // experimental experimentalResolveStyleCssClasses: vueOptions.experimentalResolveStyleCssClasses ?? 'scoped', diff --git a/vue-language-tools/vue-language-server/src/languageServerPlugin.ts b/vue-language-tools/vue-language-server/src/languageServerPlugin.ts index 40dd3da2ab..4ab3a53c88 100644 --- a/vue-language-tools/vue-language-server/src/languageServerPlugin.ts +++ b/vue-language-tools/vue-language-server/src/languageServerPlugin.ts @@ -42,13 +42,13 @@ const plugin: LanguageServerPlugin