From dd71c7646a5092b1e83de0a6057fef442fe75b9a Mon Sep 17 00:00:00 2001 From: Daniel Hazelbaker Date: Sat, 8 Oct 2022 13:15:34 -0700 Subject: [PATCH 01/10] Add support for custom file extensions to be treated as Vue files. --- .../vscode-vue-language-features/package.json | 5 +++++ .../vscode-vue-language-features/src/common.ts | 12 ++++++++++++ .../vue-language-core/src/languageModule.ts | 2 ++ .../vue-language-core/src/plugins.ts | 2 ++ .../vue-language-core/src/plugins/file-vue.ts | 10 +++++++++- vue-language-tools/vue-language-core/src/types.ts | 1 + .../src/languageServerPlugin.ts | 15 +++++++++++++++ .../vue-language-server/src/types.ts | 1 + .../src/plugins/vue-template.ts | 4 +++- 9 files changed, 50 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-vue-language-features/package.json b/extensions/vscode-vue-language-features/package.json index 05c76f080..33761eee1 100644 --- a/extensions/vscode-vue-language-features/package.json +++ b/extensions/vscode-vue-language-features/package.json @@ -314,6 +314,11 @@ "default": null, "description": "Set --max-old-space-size option on server process. If you have problem on frequently \"Request textDocument/** failed.\" error, try setting higher memory(MB) on it." }, + "volar.vueserver.additionalExtensions": { + "type":"string", + "default": "", + "description": "List any additional file extensions that should be processed as Vue files (requires restart)." + }, "volar.codeLens.references": { "type": "boolean", "default": true, diff --git a/extensions/vscode-vue-language-features/src/common.ts b/extensions/vscode-vue-language-features/src/common.ts index e763ace73..2f75d3fff 100644 --- a/extensions/vscode-vue-language-features/src/common.ts +++ b/extensions/vscode-vue-language-features/src/common.ts @@ -196,6 +196,17 @@ export function processMd() { return !!vscode.workspace.getConfiguration('volar').get('vueserver.vitePress.processMdFile'); } +function additionalExtensions() { + const extensions = vscode.workspace.getConfiguration('volar').get('vueserver.additionalExtensions'); + + if (extensions) { + return extensions.split(/,; /).map(e => e.trim()).filter(e => e !== ""); + } + else { + return []; + } +} + function getFillInitializeParams(featuresKinds: LanguageFeaturesKind[]) { return function (params: lsp.InitializeParams) { if (params.capabilities.textDocument) { @@ -258,6 +269,7 @@ function getInitializationOptions( vitePress: { processMdFile: processMd(), }, + additionalExtensions: additionalExtensions() }; return initializationOptions; } diff --git a/vue-language-tools/vue-language-core/src/languageModule.ts b/vue-language-tools/vue-language-core/src/languageModule.ts index 5ca612969..46af4c3ee 100644 --- a/vue-language-tools/vue-language-core/src/languageModule.ts +++ b/vue-language-tools/vue-language-core/src/languageModule.ts @@ -13,6 +13,7 @@ export function createLanguageModule( _vueCompilerOptions: VueCompilerOptions, exts: string[], extraPlugins: VueLanguagePlugin[] = [], + pluginOptions: Record> = {} ): embedded.LanguageModule { const vueLanguagePlugin = getDefaultVueLanguagePlugins( @@ -21,6 +22,7 @@ export function createLanguageModule( compilerOptions, _vueCompilerOptions, extraPlugins, + pluginOptions, ); // from https://github.com/johnsoncodehk/volar/pull/1543 diff --git a/vue-language-tools/vue-language-core/src/plugins.ts b/vue-language-tools/vue-language-core/src/plugins.ts index 8ef4efcb5..2f02ca53b 100644 --- a/vue-language-tools/vue-language-core/src/plugins.ts +++ b/vue-language-tools/vue-language-core/src/plugins.ts @@ -20,6 +20,7 @@ export function getDefaultVueLanguagePlugins( compilerOptions: ts.CompilerOptions, _vueCompilerOptions: VueCompilerOptions, extraPlugins: VueLanguagePlugin[] = [], + pluginOptions: Record> = {}, ) { const _plugins: VueLanguagePlugin[] = [ @@ -59,6 +60,7 @@ export function getDefaultVueLanguagePlugins( }, compilerOptions, vueCompilerOptions, + pluginOptions }; const plugins = _plugins.map(plugin => plugin(pluginCtx)).sort((a, b) => { const aOrder = a.order ?? 0; diff --git a/vue-language-tools/vue-language-core/src/plugins/file-vue.ts b/vue-language-tools/vue-language-core/src/plugins/file-vue.ts index 217e9f00b..efd206528 100644 --- a/vue-language-tools/vue-language-core/src/plugins/file-vue.ts +++ b/vue-language-tools/vue-language-core/src/plugins/file-vue.ts @@ -3,12 +3,20 @@ import { parse } from '../utils/parseSfc'; const plugin: VueLanguagePlugin = (ctx) => { + let validExtensions = ['.vue']; + + if (ctx.pluginOptions['file-vue']) { + if (ctx.pluginOptions['file-vue']['extensions']) { + validExtensions = ctx.pluginOptions['file-vue']['extensions'] as string[]; + } + } + return { version: 1, parseSFC(fileName, content) { - if (fileName.endsWith('.vue')) { + if (validExtensions.some(ext => fileName.endsWith(ext))) { return parse(content); } }, diff --git a/vue-language-tools/vue-language-core/src/types.ts b/vue-language-tools/vue-language-core/src/types.ts index 321355bed..6b0baf409 100644 --- a/vue-language-tools/vue-language-core/src/types.ts +++ b/vue-language-tools/vue-language-core/src/types.ts @@ -39,6 +39,7 @@ export type VueLanguagePlugin = (ctx: { }, compilerOptions: ts.CompilerOptions, vueCompilerOptions: ResolvedVueCompilerOptions, + pluginOptions: Record>, }) => { name?: string; version: 1; diff --git a/vue-language-tools/vue-language-server/src/languageServerPlugin.ts b/vue-language-tools/vue-language-server/src/languageServerPlugin.ts index 1068042c2..13fc94974 100644 --- a/vue-language-tools/vue-language-server/src/languageServerPlugin.ts +++ b/vue-language-tools/vue-language-server/src/languageServerPlugin.ts @@ -20,8 +20,21 @@ const plugin: LanguageServerPlugin '.' + ext.extension); + const pluginOptions = { + 'file-vue': { + extensions: exts + } + }; + return { extraFileExtensions, semanticService: { @@ -43,6 +56,8 @@ const plugin: LanguageServerPlugin Date: Tue, 18 Oct 2022 10:51:32 +0800 Subject: [PATCH 02/10] refactor: add `VueLanguagePlugin.extensions` instead of pluginOptions --- .../src/index.ts | 1 - .../vue-component-meta/src/index.ts | 1 - .../vue-language-core/src/languageModule.ts | 11 ++++----- .../vue-language-core/src/plugins.ts | 2 -- .../vue-language-core/src/plugins/file-vue.ts | 10 +------- .../vue-language-core/src/types.ts | 2 +- .../vue-language-core/src/utils/ts.ts | 1 + .../src/languageServerPlugin.ts | 24 +++++++++---------- .../vue-language-server/src/types.ts | 3 +++ .../src/documentService.ts | 1 - .../src/languageService.ts | 1 - .../vue-typescript/src/index.ts | 1 - 12 files changed, 21 insertions(+), 37 deletions(-) diff --git a/examples/vue-and-svelte-language-server/src/index.ts b/examples/vue-and-svelte-language-server/src/index.ts index db5d95f06..a7310ba22 100644 --- a/examples/vue-and-svelte-language-server/src/index.ts +++ b/examples/vue-and-svelte-language-server/src/index.ts @@ -28,7 +28,6 @@ const plugin: LanguageServerPlugin = checkerOptions.forceUseTs ? { diff --git a/vue-language-tools/vue-language-core/src/languageModule.ts b/vue-language-tools/vue-language-core/src/languageModule.ts index 46af4c3ee..d253c7923 100644 --- a/vue-language-tools/vue-language-core/src/languageModule.ts +++ b/vue-language-tools/vue-language-core/src/languageModule.ts @@ -11,18 +11,16 @@ export function createLanguageModule( rootDir: string, compilerOptions: ts.CompilerOptions, _vueCompilerOptions: VueCompilerOptions, - exts: string[], extraPlugins: VueLanguagePlugin[] = [], - pluginOptions: Record> = {} ): embedded.LanguageModule { + const vueCompilerOptions = resolveVueCompilerOptions(_vueCompilerOptions); const vueLanguagePlugin = getDefaultVueLanguagePlugins( ts, rootDir, compilerOptions, _vueCompilerOptions, extraPlugins, - pluginOptions, ); // from https://github.com/johnsoncodehk/volar/pull/1543 @@ -30,18 +28,17 @@ export function createLanguageModule( (ts as any).__VLS_pitched_resolveModuleNames = true; const resolveModuleNames = ts.resolveModuleName; ts.resolveModuleName = (...args) => { - if (args[6] === ts.ModuleKind.ESNext && exts.some(ext => args[0].endsWith(ext))) { + if (args[6] === ts.ModuleKind.ESNext && vueCompilerOptions.extensions.some(ext => args[0].endsWith(ext))) { args[6] = ts.ModuleKind.CommonJS; } return resolveModuleNames(...args); }; } - const vueCompilerOptions = resolveVueCompilerOptions(_vueCompilerOptions); const sharedTypesSnapshot = ts.ScriptSnapshot.fromString(localTypes.getTypesCode(vueCompilerOptions.target, vueCompilerOptions)); const languageModule: embedded.LanguageModule = { createSourceFile(fileName, snapshot) { - if (exts.some(ext => fileName.endsWith(ext))) { + if (vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext))) { return new VueSourceFile(fileName, snapshot, ts, vueLanguagePlugin); } }, @@ -102,7 +99,7 @@ export function createLanguageModule( return languageModule; function getSharedTypesFiles(fileNames: string[]) { - const moduleFiles = fileNames.filter(fileName => exts.some(ext => fileName.endsWith(ext))); + const moduleFiles = fileNames.filter(fileName => vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext))); const moduleFileDirs = [...new Set(moduleFiles.map(path.dirname))]; return moduleFileDirs.map(dir => path.join(dir, localTypes.typesFileName)); } diff --git a/vue-language-tools/vue-language-core/src/plugins.ts b/vue-language-tools/vue-language-core/src/plugins.ts index 2f02ca53b..8ef4efcb5 100644 --- a/vue-language-tools/vue-language-core/src/plugins.ts +++ b/vue-language-tools/vue-language-core/src/plugins.ts @@ -20,7 +20,6 @@ export function getDefaultVueLanguagePlugins( compilerOptions: ts.CompilerOptions, _vueCompilerOptions: VueCompilerOptions, extraPlugins: VueLanguagePlugin[] = [], - pluginOptions: Record> = {}, ) { const _plugins: VueLanguagePlugin[] = [ @@ -60,7 +59,6 @@ export function getDefaultVueLanguagePlugins( }, compilerOptions, vueCompilerOptions, - pluginOptions }; const plugins = _plugins.map(plugin => plugin(pluginCtx)).sort((a, b) => { const aOrder = a.order ?? 0; diff --git a/vue-language-tools/vue-language-core/src/plugins/file-vue.ts b/vue-language-tools/vue-language-core/src/plugins/file-vue.ts index efd206528..7e1f6ce0d 100644 --- a/vue-language-tools/vue-language-core/src/plugins/file-vue.ts +++ b/vue-language-tools/vue-language-core/src/plugins/file-vue.ts @@ -3,20 +3,12 @@ import { parse } from '../utils/parseSfc'; const plugin: VueLanguagePlugin = (ctx) => { - let validExtensions = ['.vue']; - - if (ctx.pluginOptions['file-vue']) { - if (ctx.pluginOptions['file-vue']['extensions']) { - validExtensions = ctx.pluginOptions['file-vue']['extensions'] as string[]; - } - } - return { version: 1, parseSFC(fileName, content) { - if (validExtensions.some(ext => fileName.endsWith(ext))) { + if (ctx.vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext))) { return parse(content); } }, diff --git a/vue-language-tools/vue-language-core/src/types.ts b/vue-language-tools/vue-language-core/src/types.ts index 526bea1aa..bb315222d 100644 --- a/vue-language-tools/vue-language-core/src/types.ts +++ b/vue-language-tools/vue-language-core/src/types.ts @@ -15,6 +15,7 @@ export type VueCompilerOptions = Partial; export interface ResolvedVueCompilerOptions { target: 2 | 2.7 | 3; + extensions: string[]; // base exts jsxTemplates: boolean; strictTemplates: boolean; skipTemplateCodegen: boolean; @@ -39,7 +40,6 @@ export type VueLanguagePlugin = (ctx: { }, compilerOptions: ts.CompilerOptions, vueCompilerOptions: ResolvedVueCompilerOptions, - pluginOptions: Record>, }) => { name?: string; version: 1; 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 dec682798..ddfc68034 100644 --- a/vue-language-tools/vue-language-core/src/utils/ts.ts +++ b/vue-language-tools/vue-language-core/src/utils/ts.ts @@ -89,6 +89,7 @@ export function resolveVueCompilerOptions(vueOptions: VueCompilerOptions): Resol ...vueOptions, target, + extensions: vueOptions.extensions ?? ['.vue'], jsxTemplates: vueOptions.jsxTemplates ?? false, strictTemplates: vueOptions.strictTemplates ?? false, skipTemplateCodegen: vueOptions.skipTemplateCodegen ?? false, diff --git a/vue-language-tools/vue-language-server/src/languageServerPlugin.ts b/vue-language-tools/vue-language-server/src/languageServerPlugin.ts index 2f3e8d746..ebb3979ec 100644 --- a/vue-language-tools/vue-language-server/src/languageServerPlugin.ts +++ b/vue-language-tools/vue-language-server/src/languageServerPlugin.ts @@ -23,19 +23,10 @@ const plugin: LanguageServerPlugin '.' + ext.extension); - - const pluginOptions = { - 'file-vue': { - extensions: exts - } - }; - return { extraFileExtensions, semanticService: { @@ -45,6 +36,7 @@ const plugin: LanguageServerPlugin vueOptions, @@ -56,9 +48,7 @@ const plugin: LanguageServerPlugin fileName.endsWith(ext))) { + if (vueExts.some(ext => fileName.endsWith(ext))) { return new vue2.VueSourceFile(fileName, snapshot, ts, vueLanguagePlugins); } }, @@ -131,6 +122,13 @@ const plugin: LanguageServerPlugin '.' + ext) ?? [], + ]; + } }; export = plugin; diff --git a/vue-language-tools/vue-language-server/src/types.ts b/vue-language-tools/vue-language-server/src/types.ts index fc11a238a..8e1c0ad09 100644 --- a/vue-language-tools/vue-language-server/src/types.ts +++ b/vue-language-tools/vue-language-server/src/types.ts @@ -7,5 +7,8 @@ export type VueServerInitializationOptions = LanguageServerInitializationOptions vitePress?: { processMdFile: boolean, }, + /** + * @example ['vue1', 'vue2'] + */ additionalExtensions?: string[], }; diff --git a/vue-language-tools/vue-language-service/src/documentService.ts b/vue-language-tools/vue-language-service/src/documentService.ts index 17d21f630..2f78cc898 100644 --- a/vue-language-tools/vue-language-service/src/documentService.ts +++ b/vue-language-tools/vue-language-service/src/documentService.ts @@ -52,7 +52,6 @@ export function createDocumentService( env.rootUri.fsPath, {}, {}, - ['.vue'], ); const languageServiceContext = embeddedLS.createDocumentServiceContext({ ts, diff --git a/vue-language-tools/vue-language-service/src/languageService.ts b/vue-language-tools/vue-language-service/src/languageService.ts index 54ce33718..f191372eb 100644 --- a/vue-language-tools/vue-language-service/src/languageService.ts +++ b/vue-language-tools/vue-language-service/src/languageService.ts @@ -130,7 +130,6 @@ export function createLanguageService( host.getCurrentDirectory(), host.getCompilationSettings(), host.getVueCompilationSettings(), - ['.vue'], ); const core = embedded.createEmbeddedLanguageServiceHost(host, [vueLanguageModule]); const languageServiceContext = embeddedLS.createLanguageServiceContext({ diff --git a/vue-language-tools/vue-typescript/src/index.ts b/vue-language-tools/vue-typescript/src/index.ts index b153c533d..7b0bd72e2 100644 --- a/vue-language-tools/vue-typescript/src/index.ts +++ b/vue-language-tools/vue-typescript/src/index.ts @@ -7,7 +7,6 @@ export function createLanguageService(host: vue.LanguageServiceHost) { host.getCurrentDirectory(), host.getCompilationSettings(), host.getVueCompilationSettings(), - ['.vue'] )]; return base.createLanguageService(host, mods); } From fadab9cf2a29a882d4e08ce3b0bf7c39c10ef1df Mon Sep 17 00:00:00 2001 From: Daniel Hazelbaker Date: Wed, 19 Oct 2022 22:33:11 -0700 Subject: [PATCH 03/10] Changed vueserver.additionalExtensions setting to be a string array. --- extensions/vscode-vue-language-features/package.json | 7 +++++-- extensions/vscode-vue-language-features/src/common.ts | 9 +-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/extensions/vscode-vue-language-features/package.json b/extensions/vscode-vue-language-features/package.json index ef5c0926a..1277ac95b 100644 --- a/extensions/vscode-vue-language-features/package.json +++ b/extensions/vscode-vue-language-features/package.json @@ -320,8 +320,11 @@ "description": "Ignore project references settings of tsconfig in language server for resolve issue #1916." }, "volar.vueserver.additionalExtensions": { - "type":"string", - "default": "", + "type":"array", + "items": { + "type": "string" + }, + "default": [], "description": "List any additional file extensions that should be processed as Vue files (requires restart)." }, "volar.codeLens.references": { diff --git a/extensions/vscode-vue-language-features/src/common.ts b/extensions/vscode-vue-language-features/src/common.ts index fe6c67210..25760a87e 100644 --- a/extensions/vscode-vue-language-features/src/common.ts +++ b/extensions/vscode-vue-language-features/src/common.ts @@ -203,14 +203,7 @@ export function noProjectReferences() { } function additionalExtensions() { - const extensions = vscode.workspace.getConfiguration('volar').get('vueserver.additionalExtensions'); - - if (extensions) { - return extensions.split(/,; /).map(e => e.trim()).filter(e => e !== ""); - } - else { - return []; - } + return vscode.workspace.getConfiguration('volar').get('vueserver.additionalExtensions') ?? []; } function getFillInitializeParams(featuresKinds: LanguageFeaturesKind[]) { From e4d2b99ba614540592d88dfe933ae6636e590e7d Mon Sep 17 00:00:00 2001 From: Daniel Hazelbaker Date: Wed, 19 Oct 2022 22:51:46 -0700 Subject: [PATCH 04/10] Add setup instructions on custom file extensions to vscode-vue-language-features README. --- .../vscode-vue-language-features/README.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/extensions/vscode-vue-language-features/README.md b/extensions/vscode-vue-language-features/README.md index 4e20da5ad..0b72870d1 100644 --- a/extensions/vscode-vue-language-features/README.md +++ b/extensions/vscode-vue-language-features/README.md @@ -158,6 +158,27 @@ defineProps() ``` +### Custom File Extensions + +Syntax highlighting and intellisense can be applied to additional file extensions beyond just `.vue`. This will need to be configured in three different places for full support. + +In VS Code settings for the Volar extension add any additional extensions you need to `Additional Extensions`. + +In your tsconfig.json file you will need to make sure your custom extension is included by TypeScript. If you have an include value for `./src/*.vue` then you would want to add an include for `./src/*.ext` as well. Next add a new key to the root of tsconfig.json called `vueCompilerOptions` with a child key of `extensions`. This should be an array that contains the `.vue` extension as well as your custom `.ext` extension. + +```json +"include": [ + "./src/*.ts", + "./src/*.vue", + "./src/*.ext", +], +"vueCompilerOptions": { + "extensions": [".vue", ".ext"] +} +``` + +Finally you need to make VS Code recognize your new extension and automatically associate it with the Vue language format. To do this you need to configure your File Associations setting to map `*.ext` to the language value `vue`. This can be done under Text Editor > Files, or with the key `files.associations`. + ## Sponsors