@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.
Files changed (34) hide show
  1. package/lib/documentRegistry.d.ts +2 -2
  2. package/lib/node/decorateLanguageService.d.ts +3 -3
  3. package/lib/node/decorateLanguageService.js +47 -42
  4. package/lib/node/decorateLanguageServiceHost.d.ts +4 -4
  5. package/lib/node/decorateLanguageServiceHost.js +18 -15
  6. package/lib/node/decorateProgram.d.ts +3 -3
  7. package/lib/node/decorateProgram.js +1 -1
  8. package/lib/node/dedupe.d.ts +1 -1
  9. package/lib/node/proxyCreateProgram.d.ts +2 -2
  10. package/lib/node/proxyCreateProgram.js +15 -13
  11. package/lib/node/transform.d.ts +7 -7
  12. package/lib/node/transform.js +12 -10
  13. package/lib/node/utils.d.ts +2 -2
  14. package/lib/node/utils.js +7 -10
  15. package/lib/protocol/createProject.d.ts +6 -3
  16. package/lib/protocol/createProject.js +129 -122
  17. package/lib/protocol/createSys.d.ts +3 -3
  18. package/lib/protocol/createSys.js +11 -11
  19. package/lib/quickstart/createAsyncLanguageServicePlugin.d.ts +3 -0
  20. package/lib/quickstart/createAsyncLanguageServicePlugin.js +89 -0
  21. package/lib/quickstart/createLanguageServicePlugin.d.ts +4 -0
  22. package/lib/quickstart/createLanguageServicePlugin.js +70 -0
  23. package/lib/{starters → quickstart}/runTsc.d.ts +2 -2
  24. package/lib/{starters → quickstart}/runTsc.js +1 -1
  25. package/lib/typescript/core.js +16 -8
  26. package/lib/typescript/path.js +42 -21
  27. package/lib/typescript/utilities.js +6 -3
  28. package/package.json +4 -4
  29. package/lib/protocol/getProgram.d.ts +0 -3
  30. package/lib/protocol/getProgram.js +0 -146
  31. package/lib/starters/createAsyncTSServerPlugin.d.ts +0 -3
  32. package/lib/starters/createAsyncTSServerPlugin.js +0 -82
  33. package/lib/starters/createTSServerPlugin.d.ts +0 -7
  34. 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, 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
- let languageServiceHost = createLanguageServiceHost();
38
- for (const language of languages) {
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 (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,22 +93,31 @@ function createLanguage(ts, sys, languages, configFileName, projectHost) {
81
93
  sys,
82
94
  projectHost,
83
95
  languageServiceHost,
84
- synchronizeFileSystem: 'sync' in sys ? () => sys.sync() : undefined,
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: projectHost.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: (options) => {
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
- syncProject();
120
- return [...new Set([
121
- ...getVirtualFileDirectories(dirName),
122
- ...sys.getDirectories(dirName),
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
- syncProject();
161
+ sync();
167
162
  return tsProjectVersion + ('version' in sys ? `:${sys.version}` : '');
168
163
  },
169
164
  getScriptFileNames() {
170
- syncProject();
165
+ sync();
171
166
  return [...tsFileRegistry.keys()];
172
167
  },
173
168
  getScriptKind(fileName) {
174
- syncSourceFile(fileName);
175
- const virtualFile = files.getVirtualFile(fileName)[0];
176
- if (virtualFile?.typescript) {
177
- return virtualFile.typescript.scriptKind;
169
+ sync();
170
+ if (extraScriptRegistry.has(fileName)) {
171
+ return extraScriptRegistry.get(fileName).scriptKind;
178
172
  }
179
- const sourceFile = files.getSourceFile(fileName);
180
- if (sourceFile?.virtualFile) {
181
- return ts.ScriptKind.Deferred;
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 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
- }
202
+ return {
203
+ languageServiceHost,
204
+ getExtraScript,
205
+ };
206
+ function getExtraScript(fileName) {
207
+ sync();
208
+ return extraScriptRegistry.get(fileName);
213
209
  }
214
- function syncProject() {
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.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
- }
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
- syncSourceFile(fileName);
256
- const virtualFile = files.getVirtualFile(fileName)[0];
257
- if (virtualFile) {
258
- return virtualFile.snapshot;
257
+ sync();
258
+ if (extraScriptRegistry.has(fileName)) {
259
+ return extraScriptRegistry.get(fileName).code.snapshot;
259
260
  }
260
- const sourceFile = files.getSourceFile(fileName);
261
- if (sourceFile && !sourceFile.virtualFile) {
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
- syncSourceFile(fileName);
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
- const virtualFile = files.getVirtualFile(fileName)[0];
272
- if (virtualFile) {
273
- if (!version.map.has(virtualFile.snapshot)) {
274
- version.map.set(virtualFile.snapshot, version.lastVersion++);
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.getSourceFile(fileName);
281
- if (sourceFile && !sourceFile.virtualFile) {
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/lib/tsserverlibrary';
3
- export declare function createSys(ts: typeof import('typescript/lib/tsserverlibrary'), env: ServiceEnvironment, currentDirectory: string): ts.System & {
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, 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);
@@ -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