@volar/typescript 2.0.0-alpha.13 → 2.0.0-alpha.14

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.
@@ -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, languages, configFileName, projectHost) {
11
- const files = (0, language_core_1.createFileProvider)(languages, sys.useCaseSensitiveFileNames, fileName => {
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.updateSourceFile(fileName, projectHost.getLanguageId(fileName), snapshot);
31
+ files.set(fileId, projectHost.getLanguageId(fileId), snapshot);
32
32
  }
33
33
  else {
34
- files.deleteSourceFile(fileName);
34
+ files.delete(fileId);
35
35
  }
36
36
  });
37
37
  let languageServiceHost = createLanguageServiceHost();
38
- for (const language of languages) {
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 (languages.some(language => language.typescript?.resolveModuleName)) {
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((moduleLiteral) => {
52
+ return moduleLiterals.map(moduleLiteral => {
53
53
  let moduleName = moduleLiteral.text;
54
- for (const language of languages) {
55
- if (language.typescript?.resolveModuleName) {
56
- moduleName = language.typescript.resolveModuleName(moduleName, sourceFile.impliedNodeFormat) ?? moduleName;
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
- return ts.resolveModuleName(moduleName, containingFile, options, languageServiceHost, moduleCache, redirectedReference, sourceFile.impliedNodeFormat);
60
- });
61
- };
62
- languageServiceHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, options, sourceFile) => {
63
- if ('version' in sys && lastSysVersion !== sys.version) {
64
- lastSysVersion = sys.version;
65
- moduleCache.clear();
66
- }
67
- return moduleNames.map((moduleName) => {
68
- for (const language of languages) {
69
- if (language.typescript?.resolveModuleName) {
70
- moduleName = language.typescript.resolveModuleName(moduleName, sourceFile?.impliedNodeFormat) ?? moduleName;
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 ts.resolveModuleName(moduleName, containingFile, options, languageServiceHost, moduleCache, redirectedReference, sourceFile?.impliedNodeFormat).resolvedModule;
85
+ return result;
74
86
  });
75
87
  };
76
88
  }
@@ -81,7 +93,6 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
81
93
  sys,
82
94
  projectHost,
83
95
  languageServiceHost,
84
- synchronizeFileSystem: 'sync' in sys ? () => sys.sync() : undefined,
85
96
  },
86
97
  };
87
98
  function createLanguageServiceHost() {
@@ -93,10 +104,18 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
93
104
  const languageServiceHost = {
94
105
  ...sys,
95
106
  getCurrentDirectory: projectHost.getCurrentDirectory,
96
- getCompilationSettings: projectHost.getCompilationSettings,
107
+ getCompilationSettings() {
108
+ const options = projectHost.getCompilationSettings();
109
+ if (languagePlugins.some(language => language.typescript?.extraFileExtensions.length)
110
+ && !options.allowNonTsExtensions) {
111
+ console.warn('`allowNonTsExtensions` must be `true`.');
112
+ options.allowNonTsExtensions ??= true;
113
+ }
114
+ return options;
115
+ },
97
116
  getLocalizedDiagnosticMessages: projectHost.getLocalizedDiagnosticMessages,
98
117
  getProjectReferences: projectHost.getProjectReferences,
99
- getDefaultLibFileName: (options) => {
118
+ getDefaultLibFileName: options => {
100
119
  try {
101
120
  return ts.getDefaultLibFilePath(options);
102
121
  }
@@ -114,54 +133,28 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
114
133
  getTypeRootsVersion: () => {
115
134
  return 'version' in sys ? sys.version : -1; // TODO: only update for /node_modules changes?
116
135
  },
117
- // need sync
118
136
  getDirectories(dirName) {
119
- syncProject();
120
- return [...new Set([
121
- ...getVirtualFileDirectories(dirName),
122
- ...sys.getDirectories(dirName),
123
- ])];
137
+ return sys.getDirectories(dirName);
138
+ },
139
+ readDirectory(dirName, extensions, excludes, includes, depth) {
140
+ const exts = new Set(extensions);
141
+ for (const languagePlugin of languagePlugins) {
142
+ for (const ext of languagePlugin.typescript?.extraFileExtensions ?? []) {
143
+ exts.add('.' + ext.extension);
144
+ }
145
+ }
146
+ extensions = [...exts];
147
+ return sys.readDirectory(dirName, extensions, excludes, includes, depth);
124
148
  },
125
149
  readFile(fileName) {
126
- syncSourceFile(fileName);
127
150
  const snapshot = getScriptSnapshot(fileName);
128
151
  if (snapshot) {
129
152
  return snapshot.getText(0, snapshot.getLength());
130
153
  }
131
154
  },
132
155
  fileExists(fileName) {
133
- syncSourceFile(fileName);
134
156
  return getScriptVersion(fileName) !== '';
135
157
  },
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
158
  getProjectVersion() {
166
159
  syncProject();
167
160
  return tsProjectVersion + ('version' in sys ? `:${sys.version}` : '');
@@ -171,14 +164,12 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
171
164
  return [...tsFileRegistry.keys()];
172
165
  },
173
166
  getScriptKind(fileName) {
174
- syncSourceFile(fileName);
175
- const virtualFile = files.getVirtualFile(fileName)[0];
176
- if (virtualFile?.typescript) {
177
- return virtualFile.typescript.scriptKind;
178
- }
179
- const sourceFile = files.getSourceFile(fileName);
180
- if (sourceFile?.virtualFile) {
181
- return ts.ScriptKind.Deferred;
167
+ const sourceFile = files.get(fileNameToFileId(fileName));
168
+ if (sourceFile?.generated) {
169
+ const tsCode = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
170
+ if (tsCode) {
171
+ return tsCode.scriptKind;
172
+ }
182
173
  }
183
174
  switch (path.extname(fileName)) {
184
175
  case '.js':
@@ -203,34 +194,26 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
203
194
  getScriptSnapshot,
204
195
  };
205
196
  return languageServiceHost;
206
- function syncSourceFile(tsFileName) {
207
- for (const language of languages) {
208
- const sourceFileName = language.typescript?.resolveSourceFileName(tsFileName);
209
- if (sourceFileName) {
210
- files.getSourceFile(sourceFileName); // trigger sync
211
- }
212
- }
213
- }
214
197
  function syncProject() {
215
198
  const newProjectVersion = projectHost.getProjectVersion?.();
216
199
  const shouldUpdate = newProjectVersion === undefined || newProjectVersion !== lastProjectVersion;
217
- if (!shouldUpdate)
200
+ if (!shouldUpdate) {
218
201
  return;
202
+ }
219
203
  lastProjectVersion = newProjectVersion;
220
204
  const newTsVirtualFileSnapshots = new Set();
221
205
  const newOtherVirtualFileSnapshots = new Set();
222
206
  const tsFileNamesSet = new Set();
223
207
  for (const fileName of projectHost.getScriptFileNames()) {
224
- const sourceFile = files.getSourceFile(fileName);
225
- if (sourceFile?.virtualFile) {
226
- for (const file of (0, language_core_2.forEachEmbeddedFile)(sourceFile.virtualFile[0])) {
227
- if (file.typescript) {
228
- newTsVirtualFileSnapshots.add(file.snapshot);
229
- tsFileNamesSet.add(file.fileName); // virtual .ts
230
- }
231
- else {
232
- newOtherVirtualFileSnapshots.add(file.snapshot);
233
- }
208
+ const sourceFile = files.get(fileNameToFileId(fileName));
209
+ if (sourceFile?.generated) {
210
+ const script = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
211
+ if (script) {
212
+ newTsVirtualFileSnapshots.add(script.code.snapshot);
213
+ tsFileNamesSet.add(fileName); // virtual .ts
214
+ }
215
+ for (const file of (0, language_core_2.forEachEmbeddedCode)(sourceFile.generated.code)) {
216
+ newOtherVirtualFileSnapshots.add(file.snapshot);
234
217
  }
235
218
  }
236
219
  else {
@@ -252,33 +235,36 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
252
235
  }
253
236
  }
254
237
  function getScriptSnapshot(fileName) {
255
- syncSourceFile(fileName);
256
- const virtualFile = files.getVirtualFile(fileName)[0];
257
- if (virtualFile) {
258
- return virtualFile.snapshot;
238
+ const sourceFile = files.get(fileNameToFileId(fileName));
239
+ if (sourceFile?.generated) {
240
+ const script = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
241
+ if (script) {
242
+ return script.code.snapshot;
243
+ }
259
244
  }
260
- const sourceFile = files.getSourceFile(fileName);
261
- if (sourceFile && !sourceFile.virtualFile) {
245
+ else if (sourceFile) {
262
246
  return sourceFile.snapshot;
263
247
  }
264
248
  }
265
249
  function getScriptVersion(fileName) {
266
- syncSourceFile(fileName);
267
250
  if (!scriptVersions.has(fileName)) {
268
251
  scriptVersions.set(fileName, { lastVersion: 0, map: new WeakMap() });
269
252
  }
270
253
  const version = scriptVersions.get(fileName);
271
- const virtualFile = files.getVirtualFile(fileName)[0];
272
- if (virtualFile) {
273
- if (!version.map.has(virtualFile.snapshot)) {
274
- version.map.set(virtualFile.snapshot, version.lastVersion++);
254
+ const sourceFile = files.get(fileNameToFileId(fileName));
255
+ if (sourceFile?.generated) {
256
+ const script = sourceFile.generated.languagePlugin.typescript?.getScript(sourceFile.generated.code);
257
+ if (script) {
258
+ if (!version.map.has(script.code.snapshot)) {
259
+ version.map.set(script.code.snapshot, version.lastVersion++);
260
+ }
261
+ return version.map.get(script.code.snapshot).toString();
275
262
  }
276
- return version.map.get(virtualFile.snapshot).toString();
277
263
  }
278
264
  const isOpenedFile = !!projectHost.getScriptSnapshot(fileName);
279
265
  if (isOpenedFile) {
280
- const sourceFile = files.getSourceFile(fileName);
281
- if (sourceFile && !sourceFile.virtualFile) {
266
+ const sourceFile = files.get(fileNameToFileId(fileName));
267
+ if (sourceFile && !sourceFile.generated) {
282
268
  if (!version.map.has(sourceFile.snapshot)) {
283
269
  version.map.set(sourceFile.snapshot, version.lastVersion++);
284
270
  }
@@ -290,27 +276,17 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
290
276
  }
291
277
  return '';
292
278
  }
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
279
  }
306
280
  }
307
281
  exports.createLanguage = createLanguage;
308
282
  function setEquals(a, b) {
309
- if (a.size !== b.size)
283
+ if (a.size !== b.size) {
310
284
  return false;
285
+ }
311
286
  for (const item of a) {
312
- if (!b.has(item))
287
+ if (!b.has(item)) {
313
288
  return false;
289
+ }
314
290
  }
315
291
  return true;
316
292
  }
@@ -1,6 +1,6 @@
1
- import type { ServiceEnvironment, Disposable } from '@volar/language-service';
1
+ import type { ServiceEnvironment, Disposable, TypeScriptProjectHost } from '@volar/language-service';
2
2
  import type * as ts from 'typescript';
3
- export declare function createSys(ts: typeof import('typescript'), env: ServiceEnvironment, currentDirectory: string): ts.System & {
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, currentDirectory) {
7
+ function createSys(ts, env, projectHost) {
8
8
  let version = 0;
9
- const rootPath = currentDirectory;
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, (dirPath) => {
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((result) => {
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((stat) => {
260
+ stat.then(stat => {
261
261
  promises.delete(promise);
262
262
  if (stat?.type === 1) {
263
263
  let file = dir.files.get(name);
@@ -6,60 +6,67 @@ const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHo
6
6
  const language_core_1 = require("@volar/language-core");
7
7
  const createTSServerPlugin_1 = require("./createTSServerPlugin");
8
8
  const externalFiles = new WeakMap();
9
+ const decoratedLanguageServices = new WeakSet();
10
+ const decoratedLanguageServiceHosts = new WeakSet();
9
11
  function createAsyncTSServerPlugin(extensions, scriptKind, loadLanguagePlugins) {
10
- return (modules) => {
12
+ return modules => {
11
13
  const { typescript: ts } = modules;
12
14
  const pluginModule = {
13
15
  create(info) {
14
- const emptySnapshot = ts.ScriptSnapshot.fromString('');
15
- const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
16
- const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
17
- const getScriptKind = info.languageServiceHost.getScriptKind?.bind(info.languageServiceHost);
18
- const getProjectVersion = info.languageServiceHost.getProjectVersion?.bind(info.languageServiceHost);
19
- let initialized = false;
20
- info.languageServiceHost.getScriptSnapshot = (fileName) => {
21
- if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
22
- return emptySnapshot;
23
- }
24
- return getScriptSnapshot(fileName);
25
- };
26
- info.languageServiceHost.getScriptVersion = (fileName) => {
27
- if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
28
- return 'initializing...';
29
- }
30
- return getScriptVersion(fileName);
31
- };
32
- if (getScriptKind) {
33
- info.languageServiceHost.getScriptKind = (fileName) => {
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 => {
34
27
  if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
35
- return scriptKind; // TODO: bypass upstream bug
28
+ return emptySnapshot;
36
29
  }
37
- return getScriptKind(fileName);
30
+ return getScriptSnapshot(fileName);
38
31
  };
39
- }
40
- if (getProjectVersion) {
41
- info.languageServiceHost.getProjectVersion = () => {
42
- if (!initialized) {
43
- return getProjectVersion() + ',initializing...';
32
+ info.languageServiceHost.getScriptVersion = fileName => {
33
+ if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
34
+ return 'initializing...';
44
35
  }
45
- return getProjectVersion();
36
+ return getScriptVersion(fileName);
46
37
  };
47
- }
48
- loadLanguagePlugins(ts, info).then(languagePlugins => {
49
- const files = (0, language_core_1.createFileProvider)(languagePlugins, ts.sys.useCaseSensitiveFileNames, (fileName) => {
50
- const snapshot = getScriptSnapshot(fileName);
51
- if (snapshot) {
52
- files.updateSourceFile(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
53
- }
54
- else {
55
- files.deleteSourceFile(fileName);
56
- }
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;
57
68
  });
58
- (0, decorateLanguageService_1.decorateLanguageService)(files, info.languageService);
59
- (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(files, info.languageServiceHost, ts, extensions);
60
- info.project.markAsDirty();
61
- initialized = true;
62
- });
69
+ }
63
70
  return info.languageService;
64
71
  },
65
72
  getExternalFiles(project, updateLevel = 0) {
@@ -1,7 +1,4 @@
1
1
  import type * as ts from 'typescript';
2
2
  import { LanguagePlugin } from '@volar/language-core';
3
- export declare function createTSServerPlugin(init: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => {
4
- languagePlugins: LanguagePlugin[];
5
- extensions: string[];
6
- }): ts.server.PluginModuleFactory;
3
+ export declare function createTSServerPlugin(init: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => LanguagePlugin[]): ts.server.PluginModuleFactory;
7
4
  export declare function arrayItemsEqual(a: string[], b: string[]): boolean;
@@ -6,25 +6,35 @@ const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHo
6
6
  const language_core_1 = require("@volar/language-core");
7
7
  const externalFiles = new WeakMap();
8
8
  const projectExternalFileExtensions = new WeakMap();
9
+ const decoratedLanguageServices = new WeakSet();
10
+ const decoratedLanguageServiceHosts = new WeakSet();
9
11
  function createTSServerPlugin(init) {
10
- return (modules) => {
12
+ return modules => {
11
13
  const { typescript: ts } = modules;
12
14
  const pluginModule = {
13
15
  create(info) {
14
- const { languagePlugins, extensions } = init(ts, info);
15
- projectExternalFileExtensions.set(info.project, extensions);
16
- const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
17
- const files = (0, language_core_1.createFileProvider)(languagePlugins, ts.sys.useCaseSensitiveFileNames, fileName => {
18
- const snapshot = getScriptSnapshot(fileName);
19
- if (snapshot) {
20
- files.updateSourceFile(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
21
- }
22
- else {
23
- files.deleteSourceFile(fileName);
24
- }
25
- });
26
- (0, decorateLanguageService_1.decorateLanguageService)(files, info.languageService);
27
- (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(files, info.languageServiceHost, ts, extensions);
16
+ if (!decoratedLanguageServices.has(info.languageService)
17
+ && !decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
18
+ decoratedLanguageServices.add(info.languageService);
19
+ decoratedLanguageServiceHosts.add(info.languageServiceHost);
20
+ const languagePlugins = init(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
+ }
28
38
  return info.languageService;
29
39
  },
30
40
  getExternalFiles(project, updateLevel = 0) {