@volar/typescript 2.0.0-alpha.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/documentRegistry.d.ts +2 -2
- package/lib/node/decorateLanguageService.d.ts +3 -3
- package/lib/node/decorateLanguageService.js +47 -42
- package/lib/node/decorateLanguageServiceHost.d.ts +4 -4
- package/lib/node/decorateLanguageServiceHost.js +18 -15
- package/lib/node/decorateProgram.d.ts +3 -3
- package/lib/node/decorateProgram.js +1 -1
- package/lib/node/dedupe.d.ts +1 -1
- package/lib/node/proxyCreateProgram.d.ts +2 -2
- package/lib/node/proxyCreateProgram.js +15 -13
- package/lib/node/transform.d.ts +7 -7
- package/lib/node/transform.js +12 -10
- package/lib/node/utils.d.ts +2 -2
- package/lib/node/utils.js +7 -10
- package/lib/protocol/createProject.d.ts +6 -3
- package/lib/protocol/createProject.js +129 -122
- package/lib/protocol/createSys.d.ts +3 -3
- package/lib/protocol/createSys.js +11 -11
- package/lib/quickstart/createAsyncLanguageServicePlugin.d.ts +3 -0
- package/lib/quickstart/createAsyncLanguageServicePlugin.js +89 -0
- package/lib/quickstart/createLanguageServicePlugin.d.ts +4 -0
- package/lib/quickstart/createLanguageServicePlugin.js +70 -0
- package/lib/{starters → quickstart}/runTsc.d.ts +2 -2
- package/lib/{starters → quickstart}/runTsc.js +1 -1
- package/lib/typescript/core.js +16 -8
- package/lib/typescript/path.js +42 -21
- package/lib/typescript/utilities.js +6 -3
- package/package.json +4 -4
- package/lib/protocol/getProgram.d.ts +0 -3
- package/lib/protocol/getProgram.js +0 -146
- package/lib/starters/createAsyncTSServerPlugin.d.ts +0 -3
- package/lib/starters/createAsyncTSServerPlugin.js +0 -82
- package/lib/starters/createTSServerPlugin.d.ts +0 -7
- package/lib/starters/createTSServerPlugin.js +0 -60
|
@@ -4,11 +4,11 @@ exports.createLanguage = void 0;
|
|
|
4
4
|
const language_core_1 = require("@volar/language-core");
|
|
5
5
|
const language_core_2 = require("@volar/language-core");
|
|
6
6
|
const path = require("path-browserify");
|
|
7
|
-
const utilities_1 = require("../typescript/utilities");
|
|
8
7
|
const scriptVersions = new Map();
|
|
9
8
|
const fsFileSnapshots = new Map();
|
|
10
|
-
function createLanguage(ts, sys,
|
|
11
|
-
const files = (0, language_core_1.
|
|
9
|
+
function createLanguage(ts, sys, languagePlugins, configFileName, projectHost, { fileIdToFileName, fileNameToFileId }) {
|
|
10
|
+
const files = (0, language_core_1.createFileRegistry)(languagePlugins, sys.useCaseSensitiveFileNames, fileId => {
|
|
11
|
+
const fileName = fileIdToFileName(fileId);
|
|
12
12
|
// opened files
|
|
13
13
|
let snapshot = projectHost.getScriptSnapshot(fileName);
|
|
14
14
|
if (!snapshot) {
|
|
@@ -28,19 +28,19 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
28
28
|
snapshot = fsFileSnapshots.get(fileName)?.[1];
|
|
29
29
|
}
|
|
30
30
|
if (snapshot) {
|
|
31
|
-
files.
|
|
31
|
+
files.set(fileId, projectHost.getLanguageId(fileId), snapshot);
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
|
-
files.
|
|
34
|
+
files.delete(fileId);
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
|
-
let languageServiceHost = createLanguageServiceHost();
|
|
38
|
-
for (const language of
|
|
37
|
+
let { languageServiceHost, getExtraScript } = createLanguageServiceHost();
|
|
38
|
+
for (const language of languagePlugins) {
|
|
39
39
|
if (language.typescript?.resolveLanguageServiceHost) {
|
|
40
40
|
languageServiceHost = language.typescript.resolveLanguageServiceHost(languageServiceHost);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
if (
|
|
43
|
+
if (languagePlugins.some(language => language.typescript?.extraFileExtensions.length)) {
|
|
44
44
|
// TODO: can this share between monorepo packages?
|
|
45
45
|
const moduleCache = ts.createModuleResolutionCache(languageServiceHost.getCurrentDirectory(), languageServiceHost.useCaseSensitiveFileNames ? s => s : s => s.toLowerCase(), languageServiceHost.getCompilationSettings());
|
|
46
46
|
let lastSysVersion = 'version' in sys ? sys.version : undefined;
|
|
@@ -49,28 +49,40 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
49
49
|
lastSysVersion = sys.version;
|
|
50
50
|
moduleCache.clear();
|
|
51
51
|
}
|
|
52
|
-
return moduleLiterals.map(
|
|
52
|
+
return moduleLiterals.map(moduleLiteral => {
|
|
53
53
|
let moduleName = moduleLiteral.text;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
let extraFileExtension;
|
|
55
|
+
let isPatchResult = false;
|
|
56
|
+
for (const language of languagePlugins) {
|
|
57
|
+
extraFileExtension = language.typescript?.extraFileExtensions.find(ext => moduleName.endsWith('.' + ext.extension))?.extension;
|
|
58
|
+
if (extraFileExtension) {
|
|
59
|
+
break;
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
const result = ts.resolveModuleName(moduleName, containingFile, options, {
|
|
63
|
+
...languageServiceHost,
|
|
64
|
+
fileExists(fileName) {
|
|
65
|
+
if (extraFileExtension && fileName.endsWith('.d.ts')) {
|
|
66
|
+
const patchResult = languageServiceHost.fileExists(fileName.slice(0, -5));
|
|
67
|
+
if (patchResult) {
|
|
68
|
+
isPatchResult = true;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return sys.fileExists(fileName);
|
|
73
|
+
},
|
|
74
|
+
}, moduleCache, redirectedReference, sourceFile.impliedNodeFormat);
|
|
75
|
+
if (isPatchResult && result.resolvedModule) {
|
|
76
|
+
result.resolvedModule.resolvedFileName = result.resolvedModule.resolvedFileName.slice(0, -5);
|
|
77
|
+
const sourceFile = files.get(fileNameToFileId(result.resolvedModule.resolvedFileName));
|
|
78
|
+
if (sourceFile?.generated) {
|
|
79
|
+
const tsCode = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
|
|
80
|
+
if (tsCode) {
|
|
81
|
+
result.resolvedModule.extension = tsCode.extension;
|
|
82
|
+
}
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
|
-
return
|
|
85
|
+
return result;
|
|
74
86
|
});
|
|
75
87
|
};
|
|
76
88
|
}
|
|
@@ -81,22 +93,31 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
81
93
|
sys,
|
|
82
94
|
projectHost,
|
|
83
95
|
languageServiceHost,
|
|
84
|
-
|
|
96
|
+
getExtraScript,
|
|
85
97
|
},
|
|
86
98
|
};
|
|
87
99
|
function createLanguageServiceHost() {
|
|
88
100
|
let lastProjectVersion;
|
|
89
101
|
let tsProjectVersion = 0;
|
|
90
102
|
let tsFileRegistry = new language_core_1.FileMap(sys.useCaseSensitiveFileNames);
|
|
103
|
+
let extraScriptRegistry = new language_core_1.FileMap(sys.useCaseSensitiveFileNames);
|
|
91
104
|
let lastTsVirtualFileSnapshots = new Set();
|
|
92
105
|
let lastOtherVirtualFileSnapshots = new Set();
|
|
93
106
|
const languageServiceHost = {
|
|
94
107
|
...sys,
|
|
95
108
|
getCurrentDirectory: projectHost.getCurrentDirectory,
|
|
96
|
-
getCompilationSettings
|
|
109
|
+
getCompilationSettings() {
|
|
110
|
+
const options = projectHost.getCompilationSettings();
|
|
111
|
+
if (languagePlugins.some(language => language.typescript?.extraFileExtensions.length)
|
|
112
|
+
&& !options.allowNonTsExtensions) {
|
|
113
|
+
console.warn('`allowNonTsExtensions` must be `true`.');
|
|
114
|
+
options.allowNonTsExtensions ??= true;
|
|
115
|
+
}
|
|
116
|
+
return options;
|
|
117
|
+
},
|
|
97
118
|
getLocalizedDiagnosticMessages: projectHost.getLocalizedDiagnosticMessages,
|
|
98
119
|
getProjectReferences: projectHost.getProjectReferences,
|
|
99
|
-
getDefaultLibFileName:
|
|
120
|
+
getDefaultLibFileName: options => {
|
|
100
121
|
try {
|
|
101
122
|
return ts.getDefaultLibFilePath(options);
|
|
102
123
|
}
|
|
@@ -114,71 +135,47 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
114
135
|
getTypeRootsVersion: () => {
|
|
115
136
|
return 'version' in sys ? sys.version : -1; // TODO: only update for /node_modules changes?
|
|
116
137
|
},
|
|
117
|
-
// need sync
|
|
118
138
|
getDirectories(dirName) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
139
|
+
return sys.getDirectories(dirName);
|
|
140
|
+
},
|
|
141
|
+
readDirectory(dirName, extensions, excludes, includes, depth) {
|
|
142
|
+
const exts = new Set(extensions);
|
|
143
|
+
for (const languagePlugin of languagePlugins) {
|
|
144
|
+
for (const ext of languagePlugin.typescript?.extraFileExtensions ?? []) {
|
|
145
|
+
exts.add('.' + ext.extension);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
extensions = [...exts];
|
|
149
|
+
return sys.readDirectory(dirName, extensions, excludes, includes, depth);
|
|
124
150
|
},
|
|
125
151
|
readFile(fileName) {
|
|
126
|
-
syncSourceFile(fileName);
|
|
127
152
|
const snapshot = getScriptSnapshot(fileName);
|
|
128
153
|
if (snapshot) {
|
|
129
154
|
return snapshot.getText(0, snapshot.getLength());
|
|
130
155
|
}
|
|
131
156
|
},
|
|
132
157
|
fileExists(fileName) {
|
|
133
|
-
syncSourceFile(fileName);
|
|
134
158
|
return getScriptVersion(fileName) !== '';
|
|
135
159
|
},
|
|
136
|
-
readDirectory(dirName, extensions, excludes, includes, depth) {
|
|
137
|
-
syncProject();
|
|
138
|
-
let matches = (0, utilities_1.matchFiles)(dirName, extensions, excludes, includes, sys?.useCaseSensitiveFileNames ?? false, projectHost.getCurrentDirectory(), depth, (dirPath) => {
|
|
139
|
-
const files = [];
|
|
140
|
-
for (const fileName of tsFileRegistry.keys()) {
|
|
141
|
-
if (fileName.toLowerCase().startsWith(dirPath.toLowerCase())) {
|
|
142
|
-
const baseName = fileName.substring(dirPath.length);
|
|
143
|
-
if (baseName.indexOf('/') === -1) {
|
|
144
|
-
files.push(baseName);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return {
|
|
149
|
-
files,
|
|
150
|
-
directories: getVirtualFileDirectories(dirPath),
|
|
151
|
-
};
|
|
152
|
-
}, sys?.realpath ? (path => sys.realpath(path)) : (path => path));
|
|
153
|
-
matches = matches.map(match => {
|
|
154
|
-
const [_, source] = files.getVirtualFile(match);
|
|
155
|
-
if (source) {
|
|
156
|
-
return source.fileName;
|
|
157
|
-
}
|
|
158
|
-
return match;
|
|
159
|
-
});
|
|
160
|
-
return [...new Set([
|
|
161
|
-
...matches,
|
|
162
|
-
...sys.readDirectory(dirName, extensions, excludes, includes, depth),
|
|
163
|
-
])];
|
|
164
|
-
},
|
|
165
160
|
getProjectVersion() {
|
|
166
|
-
|
|
161
|
+
sync();
|
|
167
162
|
return tsProjectVersion + ('version' in sys ? `:${sys.version}` : '');
|
|
168
163
|
},
|
|
169
164
|
getScriptFileNames() {
|
|
170
|
-
|
|
165
|
+
sync();
|
|
171
166
|
return [...tsFileRegistry.keys()];
|
|
172
167
|
},
|
|
173
168
|
getScriptKind(fileName) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return virtualFile.typescript.scriptKind;
|
|
169
|
+
sync();
|
|
170
|
+
if (extraScriptRegistry.has(fileName)) {
|
|
171
|
+
return extraScriptRegistry.get(fileName).scriptKind;
|
|
178
172
|
}
|
|
179
|
-
const sourceFile = files.
|
|
180
|
-
if (sourceFile?.
|
|
181
|
-
|
|
173
|
+
const sourceFile = files.get(fileNameToFileId(fileName));
|
|
174
|
+
if (sourceFile?.generated) {
|
|
175
|
+
const tsCode = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
|
|
176
|
+
if (tsCode) {
|
|
177
|
+
return tsCode.scriptKind;
|
|
178
|
+
}
|
|
182
179
|
}
|
|
183
180
|
switch (path.extname(fileName)) {
|
|
184
181
|
case '.js':
|
|
@@ -202,35 +199,40 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
202
199
|
getScriptVersion,
|
|
203
200
|
getScriptSnapshot,
|
|
204
201
|
};
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
202
|
+
return {
|
|
203
|
+
languageServiceHost,
|
|
204
|
+
getExtraScript,
|
|
205
|
+
};
|
|
206
|
+
function getExtraScript(fileName) {
|
|
207
|
+
sync();
|
|
208
|
+
return extraScriptRegistry.get(fileName);
|
|
213
209
|
}
|
|
214
|
-
function
|
|
210
|
+
function sync() {
|
|
215
211
|
const newProjectVersion = projectHost.getProjectVersion?.();
|
|
216
212
|
const shouldUpdate = newProjectVersion === undefined || newProjectVersion !== lastProjectVersion;
|
|
217
|
-
if (!shouldUpdate)
|
|
213
|
+
if (!shouldUpdate) {
|
|
218
214
|
return;
|
|
215
|
+
}
|
|
219
216
|
lastProjectVersion = newProjectVersion;
|
|
217
|
+
extraScriptRegistry.clear();
|
|
220
218
|
const newTsVirtualFileSnapshots = new Set();
|
|
221
219
|
const newOtherVirtualFileSnapshots = new Set();
|
|
222
220
|
const tsFileNamesSet = new Set();
|
|
223
221
|
for (const fileName of projectHost.getScriptFileNames()) {
|
|
224
|
-
const sourceFile = files.
|
|
225
|
-
if (sourceFile?.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
222
|
+
const sourceFile = files.get(fileNameToFileId(fileName));
|
|
223
|
+
if (sourceFile?.generated) {
|
|
224
|
+
const script = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
|
|
225
|
+
if (script) {
|
|
226
|
+
newTsVirtualFileSnapshots.add(script.code.snapshot);
|
|
227
|
+
tsFileNamesSet.add(fileName);
|
|
228
|
+
}
|
|
229
|
+
for (const extraScript of sourceFile.generated.languagePlugin.typescript?.getExtraScripts?.(fileName, sourceFile.generated.code) ?? []) {
|
|
230
|
+
newTsVirtualFileSnapshots.add(extraScript.code.snapshot);
|
|
231
|
+
tsFileNamesSet.add(extraScript.fileName);
|
|
232
|
+
extraScriptRegistry.set(extraScript.fileName, extraScript);
|
|
233
|
+
}
|
|
234
|
+
for (const code of (0, language_core_2.forEachEmbeddedCode)(sourceFile.generated.code)) {
|
|
235
|
+
newOtherVirtualFileSnapshots.add(code.snapshot);
|
|
234
236
|
}
|
|
235
237
|
}
|
|
236
238
|
else {
|
|
@@ -252,33 +254,48 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
252
254
|
}
|
|
253
255
|
}
|
|
254
256
|
function getScriptSnapshot(fileName) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return virtualFile.snapshot;
|
|
257
|
+
sync();
|
|
258
|
+
if (extraScriptRegistry.has(fileName)) {
|
|
259
|
+
return extraScriptRegistry.get(fileName).code.snapshot;
|
|
259
260
|
}
|
|
260
|
-
const sourceFile = files.
|
|
261
|
-
if (sourceFile
|
|
261
|
+
const sourceFile = files.get(fileNameToFileId(fileName));
|
|
262
|
+
if (sourceFile?.generated) {
|
|
263
|
+
const script = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
|
|
264
|
+
if (script) {
|
|
265
|
+
return script.code.snapshot;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else if (sourceFile) {
|
|
262
269
|
return sourceFile.snapshot;
|
|
263
270
|
}
|
|
264
271
|
}
|
|
265
272
|
function getScriptVersion(fileName) {
|
|
266
|
-
|
|
273
|
+
sync();
|
|
267
274
|
if (!scriptVersions.has(fileName)) {
|
|
268
275
|
scriptVersions.set(fileName, { lastVersion: 0, map: new WeakMap() });
|
|
269
276
|
}
|
|
270
277
|
const version = scriptVersions.get(fileName);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (!version.map.has(
|
|
274
|
-
version.map.set(
|
|
278
|
+
if (extraScriptRegistry.has(fileName)) {
|
|
279
|
+
const snapshot = extraScriptRegistry.get(fileName).code.snapshot;
|
|
280
|
+
if (!version.map.has(snapshot)) {
|
|
281
|
+
version.map.set(snapshot, version.lastVersion++);
|
|
282
|
+
}
|
|
283
|
+
return version.map.get(snapshot).toString();
|
|
284
|
+
}
|
|
285
|
+
const sourceFile = files.get(fileNameToFileId(fileName));
|
|
286
|
+
if (sourceFile?.generated) {
|
|
287
|
+
const script = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
|
|
288
|
+
if (script) {
|
|
289
|
+
if (!version.map.has(script.code.snapshot)) {
|
|
290
|
+
version.map.set(script.code.snapshot, version.lastVersion++);
|
|
291
|
+
}
|
|
292
|
+
return version.map.get(script.code.snapshot).toString();
|
|
275
293
|
}
|
|
276
|
-
return version.map.get(virtualFile.snapshot).toString();
|
|
277
294
|
}
|
|
278
295
|
const isOpenedFile = !!projectHost.getScriptSnapshot(fileName);
|
|
279
296
|
if (isOpenedFile) {
|
|
280
|
-
const sourceFile = files.
|
|
281
|
-
if (sourceFile && !sourceFile.
|
|
297
|
+
const sourceFile = files.get(fileNameToFileId(fileName));
|
|
298
|
+
if (sourceFile && !sourceFile.generated) {
|
|
282
299
|
if (!version.map.has(sourceFile.snapshot)) {
|
|
283
300
|
version.map.set(sourceFile.snapshot, version.lastVersion++);
|
|
284
301
|
}
|
|
@@ -290,27 +307,17 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
|
|
|
290
307
|
}
|
|
291
308
|
return '';
|
|
292
309
|
}
|
|
293
|
-
function getVirtualFileDirectories(dirName) {
|
|
294
|
-
const names = new Set();
|
|
295
|
-
for (const fileName of tsFileRegistry.keys()) {
|
|
296
|
-
if (fileName.toLowerCase().startsWith(dirName.toLowerCase())) {
|
|
297
|
-
const path = fileName.substring(dirName.length);
|
|
298
|
-
if (path.indexOf('/') >= 0) {
|
|
299
|
-
names.add(path.split('/')[0]);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return [...names];
|
|
304
|
-
}
|
|
305
310
|
}
|
|
306
311
|
}
|
|
307
312
|
exports.createLanguage = createLanguage;
|
|
308
313
|
function setEquals(a, b) {
|
|
309
|
-
if (a.size !== b.size)
|
|
314
|
+
if (a.size !== b.size) {
|
|
310
315
|
return false;
|
|
316
|
+
}
|
|
311
317
|
for (const item of a) {
|
|
312
|
-
if (!b.has(item))
|
|
318
|
+
if (!b.has(item)) {
|
|
313
319
|
return false;
|
|
320
|
+
}
|
|
314
321
|
}
|
|
315
322
|
return true;
|
|
316
323
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ServiceEnvironment, Disposable } from '@volar/language-service';
|
|
2
|
-
import type * as ts from 'typescript
|
|
3
|
-
export declare function createSys(ts: typeof import('typescript
|
|
1
|
+
import type { ServiceEnvironment, Disposable, TypeScriptProjectHost } from '@volar/language-service';
|
|
2
|
+
import type * as ts from 'typescript';
|
|
3
|
+
export declare function createSys(ts: typeof import('typescript'), env: ServiceEnvironment, projectHost: TypeScriptProjectHost): ts.System & {
|
|
4
4
|
version: number;
|
|
5
5
|
sync(): Promise<number>;
|
|
6
6
|
} & Disposable;
|
|
@@ -4,9 +4,9 @@ exports.createSys = void 0;
|
|
|
4
4
|
const path = require("path-browserify");
|
|
5
5
|
const utilities_1 = require("../typescript/utilities");
|
|
6
6
|
let currentCwd = '';
|
|
7
|
-
function createSys(ts, env,
|
|
7
|
+
function createSys(ts, env, projectHost) {
|
|
8
8
|
let version = 0;
|
|
9
|
-
const rootPath =
|
|
9
|
+
const rootPath = projectHost.getCurrentDirectory();
|
|
10
10
|
const sys = ts.sys;
|
|
11
11
|
const root = {
|
|
12
12
|
dirs: new Map(),
|
|
@@ -16,7 +16,7 @@ function createSys(ts, env, currentDirectory) {
|
|
|
16
16
|
const promises = new Set();
|
|
17
17
|
const fileWatcher = env.onDidChangeWatchedFiles?.(({ changes }) => {
|
|
18
18
|
for (const change of changes) {
|
|
19
|
-
const fileName = env.uriToFileName(change.uri);
|
|
19
|
+
const fileName = env.typescript.uriToFileName(change.uri);
|
|
20
20
|
const dirName = path.dirname(fileName);
|
|
21
21
|
const baseName = path.basename(fileName);
|
|
22
22
|
const dir = getDir(dirName);
|
|
@@ -107,7 +107,7 @@ function createSys(ts, env, currentDirectory) {
|
|
|
107
107
|
const dir = getDir(dirName);
|
|
108
108
|
if (dir.exists === undefined) {
|
|
109
109
|
dir.exists = false;
|
|
110
|
-
const result = env.fs?.stat(env.fileNameToUri(dirName));
|
|
110
|
+
const result = env.fs?.stat(env.typescript.fileNameToUri(dirName));
|
|
111
111
|
if (typeof result === 'object' && 'then' in result) {
|
|
112
112
|
const promise = result;
|
|
113
113
|
promises.add(promise);
|
|
@@ -148,7 +148,7 @@ function createSys(ts, env, currentDirectory) {
|
|
|
148
148
|
return exists();
|
|
149
149
|
}
|
|
150
150
|
function handleStat(fileName, file) {
|
|
151
|
-
const result = env.fs?.stat(env.fileNameToUri(fileName));
|
|
151
|
+
const result = env.fs?.stat(env.typescript.fileNameToUri(fileName));
|
|
152
152
|
if (typeof result === 'object' && 'then' in result) {
|
|
153
153
|
const promise = result;
|
|
154
154
|
promises.add(promise);
|
|
@@ -184,7 +184,7 @@ function createSys(ts, env, currentDirectory) {
|
|
|
184
184
|
}
|
|
185
185
|
function readDirectory(dirName, extensions, excludes, includes, depth) {
|
|
186
186
|
dirName = resolvePath(dirName);
|
|
187
|
-
const matches = (0, utilities_1.matchFiles)(dirName, extensions, excludes, includes, sys?.useCaseSensitiveFileNames ?? false, rootPath, depth,
|
|
187
|
+
const matches = (0, utilities_1.matchFiles)(dirName, extensions, excludes, includes, sys?.useCaseSensitiveFileNames ?? false, rootPath, depth, dirPath => {
|
|
188
188
|
dirPath = resolvePath(dirPath);
|
|
189
189
|
readDirectoryWorker(dirPath);
|
|
190
190
|
const dir = getDir(dirPath);
|
|
@@ -205,7 +205,7 @@ function createSys(ts, env, currentDirectory) {
|
|
|
205
205
|
return;
|
|
206
206
|
}
|
|
207
207
|
file.requestedText = true;
|
|
208
|
-
const uri = env.fileNameToUri(fileName);
|
|
208
|
+
const uri = env.typescript.fileNameToUri(fileName);
|
|
209
209
|
const result = env.fs?.readFile(uri, encoding);
|
|
210
210
|
if (typeof result === 'object' && 'then' in result) {
|
|
211
211
|
const promise = result;
|
|
@@ -231,11 +231,11 @@ function createSys(ts, env, currentDirectory) {
|
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
233
233
|
dir.requestedRead = true;
|
|
234
|
-
const result = env.fs?.readDirectory(env.fileNameToUri(dirName || '.'));
|
|
234
|
+
const result = env.fs?.readDirectory(env.typescript.fileNameToUri(dirName || '.'));
|
|
235
235
|
if (typeof result === 'object' && 'then' in result) {
|
|
236
236
|
const promise = result;
|
|
237
237
|
promises.add(promise);
|
|
238
|
-
result.then(
|
|
238
|
+
result.then(result => {
|
|
239
239
|
promises.delete(promise);
|
|
240
240
|
if (onReadDirectoryResult(dirName, dir, result)) {
|
|
241
241
|
version++;
|
|
@@ -253,11 +253,11 @@ function createSys(ts, env, currentDirectory) {
|
|
|
253
253
|
for (const [name, _fileType] of result) {
|
|
254
254
|
let fileType = _fileType;
|
|
255
255
|
if (fileType === 64) {
|
|
256
|
-
const stat = env.fs?.stat(env.fileNameToUri(dirName + '/' + name));
|
|
256
|
+
const stat = env.fs?.stat(env.typescript.fileNameToUri(dirName + '/' + name));
|
|
257
257
|
if (typeof stat === 'object' && 'then' in stat) {
|
|
258
258
|
const promise = stat;
|
|
259
259
|
promises.add(promise);
|
|
260
|
-
stat.then(
|
|
260
|
+
stat.then(stat => {
|
|
261
261
|
promises.delete(promise);
|
|
262
262
|
if (stat?.type === 1) {
|
|
263
263
|
let file = dir.files.get(name);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type * as ts from 'typescript';
|
|
2
|
+
import { LanguagePlugin } from '@volar/language-core';
|
|
3
|
+
export declare function createAsyncLanguageServicePlugin(extensions: string[], scriptKind: ts.ScriptKind, loadLanguagePlugins: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => Promise<LanguagePlugin[]>): ts.server.PluginModuleFactory;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAsyncLanguageServicePlugin = void 0;
|
|
4
|
+
const decorateLanguageService_1 = require("../node/decorateLanguageService");
|
|
5
|
+
const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
|
|
6
|
+
const language_core_1 = require("@volar/language-core");
|
|
7
|
+
const createLanguageServicePlugin_1 = require("./createLanguageServicePlugin");
|
|
8
|
+
const externalFiles = new WeakMap();
|
|
9
|
+
const decoratedLanguageServices = new WeakSet();
|
|
10
|
+
const decoratedLanguageServiceHosts = new WeakSet();
|
|
11
|
+
function createAsyncLanguageServicePlugin(extensions, scriptKind, loadLanguagePlugins) {
|
|
12
|
+
return modules => {
|
|
13
|
+
const { typescript: ts } = modules;
|
|
14
|
+
const pluginModule = {
|
|
15
|
+
create(info) {
|
|
16
|
+
if (!decoratedLanguageServices.has(info.languageService)
|
|
17
|
+
&& !decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
|
|
18
|
+
decoratedLanguageServices.add(info.languageService);
|
|
19
|
+
decoratedLanguageServiceHosts.add(info.languageServiceHost);
|
|
20
|
+
const emptySnapshot = ts.ScriptSnapshot.fromString('');
|
|
21
|
+
const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
22
|
+
const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
|
|
23
|
+
const getScriptKind = info.languageServiceHost.getScriptKind?.bind(info.languageServiceHost);
|
|
24
|
+
const getProjectVersion = info.languageServiceHost.getProjectVersion?.bind(info.languageServiceHost);
|
|
25
|
+
let initialized = false;
|
|
26
|
+
info.languageServiceHost.getScriptSnapshot = fileName => {
|
|
27
|
+
if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
28
|
+
return emptySnapshot;
|
|
29
|
+
}
|
|
30
|
+
return getScriptSnapshot(fileName);
|
|
31
|
+
};
|
|
32
|
+
info.languageServiceHost.getScriptVersion = fileName => {
|
|
33
|
+
if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
34
|
+
return 'initializing...';
|
|
35
|
+
}
|
|
36
|
+
return getScriptVersion(fileName);
|
|
37
|
+
};
|
|
38
|
+
if (getScriptKind) {
|
|
39
|
+
info.languageServiceHost.getScriptKind = fileName => {
|
|
40
|
+
if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
41
|
+
return scriptKind; // TODO: bypass upstream bug
|
|
42
|
+
}
|
|
43
|
+
return getScriptKind(fileName);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (getProjectVersion) {
|
|
47
|
+
info.languageServiceHost.getProjectVersion = () => {
|
|
48
|
+
if (!initialized) {
|
|
49
|
+
return getProjectVersion() + ',initializing...';
|
|
50
|
+
}
|
|
51
|
+
return getProjectVersion();
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
loadLanguagePlugins(ts, info).then(languagePlugins => {
|
|
55
|
+
const files = (0, language_core_1.createFileRegistry)(languagePlugins, ts.sys.useCaseSensitiveFileNames, fileName => {
|
|
56
|
+
const snapshot = getScriptSnapshot(fileName);
|
|
57
|
+
if (snapshot) {
|
|
58
|
+
files.set(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
files.delete(fileName);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
(0, decorateLanguageService_1.decorateLanguageService)(files, info.languageService);
|
|
65
|
+
(0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(files, info.languageServiceHost, ts);
|
|
66
|
+
info.project.markAsDirty();
|
|
67
|
+
initialized = true;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return info.languageService;
|
|
71
|
+
},
|
|
72
|
+
getExternalFiles(project, updateLevel = 0) {
|
|
73
|
+
if (updateLevel >= (1)
|
|
74
|
+
|| !externalFiles.has(project)) {
|
|
75
|
+
const oldFiles = externalFiles.get(project);
|
|
76
|
+
const newFiles = (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions);
|
|
77
|
+
externalFiles.set(project, newFiles);
|
|
78
|
+
if (oldFiles && !(0, createLanguageServicePlugin_1.arrayItemsEqual)(oldFiles, newFiles)) {
|
|
79
|
+
project.refreshDiagnostics();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return externalFiles.get(project);
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
return pluginModule;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
exports.createAsyncLanguageServicePlugin = createAsyncLanguageServicePlugin;
|
|
89
|
+
//# sourceMappingURL=createAsyncLanguageServicePlugin.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type * as ts from 'typescript';
|
|
2
|
+
import { LanguagePlugin } from '@volar/language-core';
|
|
3
|
+
export declare function createLanguageServicePlugin(loadLanguagePlugins: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => LanguagePlugin[]): ts.server.PluginModuleFactory;
|
|
4
|
+
export declare function arrayItemsEqual(a: string[], b: string[]): boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.arrayItemsEqual = exports.createLanguageServicePlugin = void 0;
|
|
4
|
+
const decorateLanguageService_1 = require("../node/decorateLanguageService");
|
|
5
|
+
const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
|
|
6
|
+
const language_core_1 = require("@volar/language-core");
|
|
7
|
+
const externalFiles = new WeakMap();
|
|
8
|
+
const projectExternalFileExtensions = new WeakMap();
|
|
9
|
+
const decoratedLanguageServices = new WeakSet();
|
|
10
|
+
const decoratedLanguageServiceHosts = new WeakSet();
|
|
11
|
+
function createLanguageServicePlugin(loadLanguagePlugins) {
|
|
12
|
+
return modules => {
|
|
13
|
+
const { typescript: ts } = modules;
|
|
14
|
+
const pluginModule = {
|
|
15
|
+
create(info) {
|
|
16
|
+
if (!decoratedLanguageServices.has(info.languageService)
|
|
17
|
+
&& !decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
|
|
18
|
+
decoratedLanguageServices.add(info.languageService);
|
|
19
|
+
decoratedLanguageServiceHosts.add(info.languageServiceHost);
|
|
20
|
+
const languagePlugins = loadLanguagePlugins(ts, info);
|
|
21
|
+
const extensions = languagePlugins
|
|
22
|
+
.map(plugin => plugin.typescript?.extraFileExtensions.map(ext => '.' + ext.extension) ?? [])
|
|
23
|
+
.flat();
|
|
24
|
+
projectExternalFileExtensions.set(info.project, extensions);
|
|
25
|
+
const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
26
|
+
const files = (0, language_core_1.createFileRegistry)(languagePlugins, ts.sys.useCaseSensitiveFileNames, fileName => {
|
|
27
|
+
const snapshot = getScriptSnapshot(fileName);
|
|
28
|
+
if (snapshot) {
|
|
29
|
+
files.set(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
files.delete(fileName);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
(0, decorateLanguageService_1.decorateLanguageService)(files, info.languageService);
|
|
36
|
+
(0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(files, info.languageServiceHost, ts);
|
|
37
|
+
}
|
|
38
|
+
return info.languageService;
|
|
39
|
+
},
|
|
40
|
+
getExternalFiles(project, updateLevel = 0) {
|
|
41
|
+
if (updateLevel >= (1)
|
|
42
|
+
|| !externalFiles.has(project)) {
|
|
43
|
+
const oldFiles = externalFiles.get(project);
|
|
44
|
+
const newFiles = (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, projectExternalFileExtensions.get(project));
|
|
45
|
+
externalFiles.set(project, newFiles);
|
|
46
|
+
if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) {
|
|
47
|
+
project.refreshDiagnostics();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return externalFiles.get(project);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
return pluginModule;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
exports.createLanguageServicePlugin = createLanguageServicePlugin;
|
|
57
|
+
function arrayItemsEqual(a, b) {
|
|
58
|
+
if (a.length !== b.length) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const set = new Set(a);
|
|
62
|
+
for (const file of b) {
|
|
63
|
+
if (!set.has(file)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
exports.arrayItemsEqual = arrayItemsEqual;
|
|
70
|
+
//# sourceMappingURL=createLanguageServicePlugin.js.map
|