-
-
Notifications
You must be signed in to change notification settings - Fork 371
/
nodeServer.ts
152 lines (126 loc) · 5.36 KB
/
nodeServer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { createConnection, createNodeServer, createSimpleProjectProvider, createTypeScriptProjectProvider } from '@volar/language-server/node';
import { ServerProject } from '@volar/language-server';
import * as vue2 from '@vue/language-core';
import { VueCompilerOptions } from '@vue/language-core';
import * as nameCasing from '@vue/language-service';
import * as vue from '@vue/language-service';
import * as componentMeta from 'vue-component-meta/out/base';
import { DetectNameCasingRequest, GetComponentMeta, GetConvertAttrCasingEditsRequest, GetConvertTagCasingEditsRequest, ParseSFCRequest } from './protocol';
import { VueInitializationOptions } from './types';
import { createSys } from '@volar/typescript';
const connection = createConnection();
const server = createNodeServer(connection);
const checkers = new WeakMap<ServerProject, componentMeta.ComponentMetaChecker>();
const envToVueOptions = new WeakMap<vue.ServiceEnvironment, VueCompilerOptions>();
connection.listen();
connection.onInitialize(params => {
const options: VueInitializationOptions = params.initializationOptions;
const vueFileExtensions: string[] = ['vue'];
if (options.vue?.additionalExtensions) {
for (const additionalExtension of options.vue.additionalExtensions) {
vueFileExtensions.push(additionalExtension);
}
}
return server.initialize(
params,
options.vue?.hybridMode ? createSimpleProjectProvider : createTypeScriptProjectProvider,
{
watchFileExtensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'json', ...vueFileExtensions],
getServicePlugins() {
const ts = getTsLib();
const services = vue.resolveServices({}, ts, env => envToVueOptions.get(env)!);
return Object.values(services);
},
async getLanguagePlugins(serviceEnv, projectContext) {
const ts = getTsLib();
const [commandLine, vueOptions] = await parseCommandLine();
const resolvedVueOptions = vue.resolveVueCompilerOptions(vueOptions);
const languages = vue.resolveLanguages({}, ts, commandLine?.options ?? {}, resolvedVueOptions, options.codegenStack);
envToVueOptions.set(serviceEnv, resolvedVueOptions);
return Object.values(languages);
async function parseCommandLine() {
let commandLine: vue2.ParsedCommandLine | undefined;
let vueOptions: Partial<vue.VueCompilerOptions> = {};
if (projectContext.typescript) {
const sys = createSys(ts, serviceEnv, serviceEnv.uriToFileName(serviceEnv.workspaceFolder.toString()));
let sysVersion: number | undefined;
let newSysVersion = await sys.sync();
while (sysVersion !== newSysVersion) {
sysVersion = newSysVersion;
if (projectContext.typescript.configFileName) {
commandLine = vue2.createParsedCommandLine(ts, sys, projectContext.typescript.configFileName);
}
newSysVersion = await sys.sync();
}
}
if (commandLine) {
vueOptions = commandLine.vueOptions;
}
vueOptions.extensions = [
...vueOptions.extensions ?? ['.vue'],
...vueFileExtensions.map(ext => '.' + ext),
];
vueOptions.extensions = [...new Set(vueOptions.extensions)];
return [commandLine, vueOptions] as const;
}
},
},
);
});
connection.onInitialized(() => {
server.initialized();
});
connection.onShutdown(() => {
server.shutdown();
});
connection.onRequest(ParseSFCRequest.type, params => {
return vue2.parse(params);
});
connection.onRequest(DetectNameCasingRequest.type, async params => {
const languageService = await getService(params.textDocument.uri);
if (languageService) {
return nameCasing.detect(getTsLib(), languageService.context, params.textDocument.uri, envToVueOptions.get(languageService.context.env)!);
}
});
connection.onRequest(GetConvertTagCasingEditsRequest.type, async params => {
const languageService = await getService(params.textDocument.uri);
if (languageService) {
return nameCasing.convertTagName(getTsLib(), languageService.context, params.textDocument.uri, params.casing, envToVueOptions.get(languageService.context.env)!);
}
});
connection.onRequest(GetConvertAttrCasingEditsRequest.type, async params => {
const languageService = await getService(params.textDocument.uri);
if (languageService) {
const vueOptions = envToVueOptions.get(languageService.context.env);
if (vueOptions) {
return nameCasing.convertAttrName(getTsLib(), languageService.context, params.textDocument.uri, params.casing, envToVueOptions.get(languageService.context.env)!);
}
}
});
connection.onRequest(GetComponentMeta.type, async params => {
const project = await server.projects.getProject(params.uri);
const langaugeService = project.getLanguageService();
let checker = checkers.get(project);
if (!checker) {
checker = componentMeta.baseCreate(
getTsLib(),
langaugeService.context.language.typescript!.configFileName,
langaugeService.context.language.typescript!.projectHost,
envToVueOptions.get(langaugeService.context.env)!,
{},
langaugeService.context.language.typescript!.languageServiceHost.getCurrentDirectory() + '/tsconfig.json.global.vue',
);
checkers.set(project, checker);
}
return checker?.getComponentMeta(langaugeService.context.env.uriToFileName(params.uri));
});
function getTsLib() {
const ts = server.modules.typescript;
if (!ts) {
throw 'typescript not found';
}
return ts;
}
async function getService(uri: string) {
return (await server.projects.getProject(uri)).getLanguageService();
}