/
proxy.ts
131 lines (114 loc) · 3.61 KB
/
proxy.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
import * as ts from 'typescript/lib/tsserverlibrary';
import * as vue from '@volar/vue-language-core';
import * as vueTs from '@volar/vue-typescript';
export function createProgramProxy(
options: ts.CreateProgramOptions, // rootNamesOrOptions: readonly string[] | CreateProgramOptions,
_options?: ts.CompilerOptions,
_host?: ts.CompilerHost,
_oldProgram?: ts.Program,
_configFileParsingDiagnostics?: readonly ts.Diagnostic[],
) {
if (!options.options.noEmit && !options.options.emitDeclarationOnly)
return doThrow('js emit is not support');
if (!options.options.noEmit && options.options.noEmitOnError)
return doThrow('noEmitOnError is not support');
if (!options.host)
return doThrow('!options.host');
let program = options.oldProgram as any;
if (!program) {
const ctx = {
projectVersion: 0,
options: options,
};
const vueCompilerOptions = getVueCompilerOptions();
const scripts = new Map<string, {
projectVersion: number,
modifiedTime: number,
scriptSnapshot: ts.IScriptSnapshot,
version: string,
}>();
const vueLsHost = new Proxy(<vue.LanguageServiceHost>{
resolveModuleNames: undefined, // avoid failed with tsc built-in fileExists
writeFile: (fileName, content) => {
if (fileName.indexOf('__VLS_') === -1) {
ctx.options.host!.writeFile(fileName, content, false);
}
},
getCompilationSettings: () => ctx.options.options,
getVueCompilationSettings: () => vueCompilerOptions,
getScriptFileNames: () => {
return ctx.options.rootNames as string[];
},
getScriptVersion,
getScriptSnapshot,
getProjectVersion: () => {
return ctx.projectVersion.toString();
},
getProjectReferences: () => ctx.options.projectReferences,
getTypeScriptModule: () => ts,
isTsc: true,
}, {
get: (target, property) => {
if (property in target) {
return target[property as keyof vue.LanguageServiceHost];
}
return ctx.options.host![property as keyof ts.CompilerHost];
},
});
const vueTsLs = vueTs.createLanguageService(vueLsHost);
program = vueTsLs.getProgram();
program.__VLS_ctx = ctx;
function getVueCompilerOptions(): vue.VueCompilerOptions {
const tsConfig = ctx.options.options.configFilePath;
if (typeof tsConfig === 'string') {
return vue.createParsedCommandLine(ts, ts.sys, tsConfig, []).vueOptions;
}
return {};
}
function getScriptVersion(fileName: string) {
return getScript(fileName)?.version ?? '';
}
function getScriptSnapshot(fileName: string) {
return getScript(fileName)?.scriptSnapshot;
}
function getScript(fileName: string) {
const script = scripts.get(fileName);
if (script?.projectVersion === ctx.projectVersion) {
return script;
}
const modifiedTime = ts.sys.getModifiedTime?.(fileName)?.valueOf() ?? 0;
if (script?.modifiedTime === modifiedTime) {
return script;
}
if (ctx.options.host!.fileExists(fileName)) {
const fileContent = ctx.options.host!.readFile(fileName);
if (fileContent !== undefined) {
const script = {
projectVersion: ctx.projectVersion,
modifiedTime,
scriptSnapshot: ts.ScriptSnapshot.fromString(fileContent),
version: ctx.options.host!.createHash?.(fileContent) ?? fileContent,
};
scripts.set(fileName, script);
return script;
}
}
}
}
else {
program.__VLS_ctx.options = options;
program.__VLS_ctx.projectVersion++;
}
for (const rootName of options.rootNames) {
// register file watchers
options.host.getSourceFile(rootName, ts.ScriptTarget.ESNext);
}
return program;
}
export function loadTsLib() {
return ts;
}
function doThrow(msg: string) {
console.error(msg);
throw msg;
}