@volar/typescript 2.4.0-alpha.9 → 2.4.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/index.d.ts CHANGED
@@ -18,8 +18,10 @@ declare module '@volar/language-service' {
18
18
  };
19
19
  languageServiceHost: ts.LanguageServiceHost;
20
20
  getExtraServiceScript(fileName: string): TypeScriptExtraServiceScript | undefined;
21
- asUri(fileName: string): URI;
22
- asFileName(uri: URI): string;
21
+ uriConverter: {
22
+ asUri(fileName: string): URI;
23
+ asFileName(uri: URI): string;
24
+ };
23
25
  };
24
26
  }
25
27
  }
@@ -57,8 +57,8 @@ function proxyCreateProgram(ts, original, create) {
57
57
  language = (0, language_core_1.createLanguage)([
58
58
  ...languagePlugins,
59
59
  { getLanguageId: common_1.resolveFileLanguageId },
60
- ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
61
- if (!sourceFileSnapshots.has(fileName)) {
60
+ ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), (fileName, includeFsFiles) => {
61
+ if (includeFsFiles && !sourceFileSnapshots.has(fileName)) {
62
62
  const sourceFileText = originalHost.readFile(fileName);
63
63
  if (sourceFileText !== undefined) {
64
64
  sourceFileSnapshots.set(fileName, [undefined, {
@@ -125,12 +125,16 @@ function proxyCreateProgram(ts, original, create) {
125
125
  if (sourceScript.generated?.languagePlugin.typescript) {
126
126
  const { getServiceScript, getExtraServiceScripts } = sourceScript.generated.languagePlugin.typescript;
127
127
  const serviceScript = getServiceScript(sourceScript.generated.root);
128
- if (serviceScript && !serviceScript.preventLeadingOffset) {
129
- let patchedText = originalSourceFile.text.split('\n').map(line => ' '.repeat(line.length)).join('\n');
130
- let scriptKind = ts.ScriptKind.TS;
131
- scriptKind = serviceScript.scriptKind;
132
- patchedText += serviceScript.code.snapshot.getText(0, serviceScript.code.snapshot.getLength());
133
- const parsedSourceFile = ts.createSourceFile(fileName, patchedText, languageVersionOrOptions, undefined, scriptKind);
128
+ if (serviceScript) {
129
+ let virtualContents;
130
+ if (!serviceScript.preventLeadingOffset) {
131
+ virtualContents = originalSourceFile.text.split('\n').map(line => ' '.repeat(line.length)).join('\n')
132
+ + serviceScript.code.snapshot.getText(0, serviceScript.code.snapshot.getLength());
133
+ }
134
+ else {
135
+ virtualContents = serviceScript.code.snapshot.getText(0, serviceScript.code.snapshot.getLength());
136
+ }
137
+ const parsedSourceFile = ts.createSourceFile(fileName, virtualContents, languageVersionOrOptions, undefined, serviceScript.scriptKind);
134
138
  // @ts-expect-error
135
139
  parsedSourceFile.version = originalSourceFile.version;
136
140
  parsedSourceFiles.set(originalSourceFile, parsedSourceFile);
@@ -29,6 +29,7 @@ function createProxyLanguageService(languageService) {
29
29
  case 'getDocumentHighlights': return getDocumentHighlights(language, target[p]);
30
30
  case 'getApplicableRefactors': return getApplicableRefactors(language, target[p]);
31
31
  case 'getEditsForRefactor': return getEditsForRefactor(language, target[p]);
32
+ case 'getCombinedCodeFix': return getCombinedCodeFix(language, target[p]);
32
33
  case 'getRenameInfo': return getRenameInfo(language, target[p]);
33
34
  case 'getCodeFixesAtPosition': return getCodeFixesAtPosition(language, target[p]);
34
35
  case 'getEncodedSemanticClassifications': return getEncodedSemanticClassifications(language, target[p]);
@@ -46,6 +47,7 @@ function createProxyLanguageService(languageService) {
46
47
  case 'getCompletionEntryDetails': return getCompletionEntryDetails(language, target[p]);
47
48
  case 'provideInlayHints': return provideInlayHints(language, target[p]);
48
49
  case 'getFileReferences': return getFileReferences(language, target[p]);
50
+ case 'getNavigateToItems': return getNavigateToItems(language, target[p]);
49
51
  }
50
52
  };
51
53
  },
@@ -467,6 +469,13 @@ function getEditsForRefactor(language, getEditsForRefactor) {
467
469
  }
468
470
  };
469
471
  }
472
+ function getCombinedCodeFix(language, getCombinedCodeFix) {
473
+ return (...args) => {
474
+ const codeActions = getCombinedCodeFix(...args);
475
+ codeActions.changes = (0, transform_1.transformFileTextChanges)(language, codeActions.changes, language_core_1.isCodeActionsEnabled);
476
+ return codeActions;
477
+ };
478
+ }
470
479
  function getRenameInfo(language, getRenameInfo) {
471
480
  return (filePath, position, options) => {
472
481
  const fileName = filePath.replace(windowsPathReg, '/');
@@ -861,6 +870,15 @@ function getFileReferences(language, getFileReferences) {
861
870
  return (0, dedupe_1.dedupeDocumentSpans)(resolved);
862
871
  };
863
872
  }
873
+ function getNavigateToItems(language, getNavigateToItems) {
874
+ return (...args) => {
875
+ const unresolved = getNavigateToItems(...args);
876
+ const resolved = unresolved
877
+ .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isReferencesEnabled))
878
+ .filter(s => !!s);
879
+ return (0, dedupe_1.dedupeDocumentSpans)(resolved);
880
+ };
881
+ }
864
882
  function linkedCodeFeatureWorker(language, fileName, position, filter, worker, getLinkedCodes) {
865
883
  const results = [];
866
884
  const processedFilePositions = new Set();
@@ -46,7 +46,7 @@ function transformDiagnostic(language, diagnostic, program, isTsc) {
46
46
  const [sourceSpanFileName, sourceSpan] = transformTextSpan(undefined, language, serviceScript, {
47
47
  start: diagnostic.start,
48
48
  length: diagnostic.length
49
- }, language_core_1.shouldReportDiagnostics) ?? [];
49
+ }, data => (0, language_core_1.shouldReportDiagnostics)(data, String(diagnostic.source), String(diagnostic.code))) ?? [];
50
50
  const actualDiagnosticFile = sourceSpanFileName
51
51
  ? diagnostic.file.fileName === sourceSpanFileName
52
52
  ? diagnostic.file
@@ -2,7 +2,7 @@ import { Language } from '@volar/language-core';
2
2
  import type * as ts from 'typescript';
3
3
  import type { TypeScriptExtraServiceScript } from '../..';
4
4
  import type { createSys } from './createSys';
5
- export interface TypeScriptProjectHost extends Pick<ts.LanguageServiceHost, 'getLocalizedDiagnosticMessages' | 'getCurrentDirectory' | 'getCompilationSettings' | 'getProjectReferences' | 'getScriptFileNames' | 'getProjectVersion' | 'getScriptSnapshot'> {
5
+ export interface TypeScriptProjectHost extends Pick<ts.LanguageServiceHost, 'getLocalizedDiagnosticMessages' | 'getCurrentDirectory' | 'getCompilationSettings' | 'getProjectReferences' | 'getScriptFileNames' | 'getProjectVersion'> {
6
6
  }
7
7
  export declare function createLanguageServiceHost<T>(ts: typeof import('typescript'), sys: ReturnType<typeof createSys> | ts.System, language: Language<T>, asScriptId: (fileName: string) => T, projectHost: TypeScriptProjectHost): {
8
8
  languageServiceHost: ts.LanguageServiceHost;
@@ -247,15 +247,12 @@ function createLanguageServiceHost(ts, sys, language, asScriptId, projectHost) {
247
247
  return version.map.get(serviceScript.code.snapshot).toString();
248
248
  }
249
249
  }
250
- const isOpenedFile = !!projectHost.getScriptSnapshot(fileName);
251
- if (isOpenedFile) {
252
- const sourceScript = language.scripts.get(asScriptId(fileName));
253
- if (sourceScript && !sourceScript.generated) {
254
- if (!version.map.has(sourceScript.snapshot)) {
255
- version.map.set(sourceScript.snapshot, version.lastVersion++);
256
- }
257
- return version.map.get(sourceScript.snapshot).toString();
250
+ const openedFile = language.scripts.get(asScriptId(fileName), false);
251
+ if (openedFile && !openedFile.generated) {
252
+ if (!version.map.has(openedFile.snapshot)) {
253
+ version.map.set(openedFile.snapshot, version.lastVersion++);
258
254
  }
255
+ return version.map.get(openedFile.snapshot).toString();
259
256
  }
260
257
  if (sys.fileExists(fileName)) {
261
258
  return sys.getModifiedTime?.(fileName)?.valueOf().toString() ?? '0';
@@ -1,7 +1,7 @@
1
1
  import type { LanguageServiceEnvironment, Disposable } from '@volar/language-service';
2
2
  import type * as ts from 'typescript';
3
3
  import { URI } from 'vscode-uri';
4
- export declare function createSys(sys: ts.System | undefined, env: LanguageServiceEnvironment, getCurrentDirectory: () => string, uriConverter: {
4
+ export declare function createSys(sys: ts.System | undefined, env: Pick<LanguageServiceEnvironment, 'onDidChangeWatchedFiles' | 'fs'>, getCurrentDirectory: () => string, uriConverter: {
5
5
  asUri(fileName: string): URI;
6
6
  asFileName(uri: URI): string;
7
7
  }): ts.System & {
@@ -1,6 +1,6 @@
1
1
  import { Language, LanguagePlugin } from '@volar/language-core';
2
2
  import type * as ts from 'typescript';
3
- export declare function createAsyncLanguageServicePlugin(extensions: string[], scriptKind: ts.ScriptKind, create: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => Promise<{
3
+ export declare function createAsyncLanguageServicePlugin(extensions: string[], getScriptKindForExtraExtensions: ts.ScriptKind | ((fileName: string) => ts.ScriptKind), create: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => Promise<{
4
4
  languagePlugins: LanguagePlugin<string>[];
5
5
  setup?: (language: Language<string>) => void;
6
6
  }>): ts.server.PluginModuleFactory;
@@ -6,7 +6,7 @@ const common_1 = require("../common");
6
6
  const proxyLanguageService_1 = require("../node/proxyLanguageService");
7
7
  const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
8
8
  const createLanguageServicePlugin_1 = require("./createLanguageServicePlugin");
9
- function createAsyncLanguageServicePlugin(extensions, scriptKind, create) {
9
+ function createAsyncLanguageServicePlugin(extensions, getScriptKindForExtraExtensions, create) {
10
10
  return modules => {
11
11
  const { typescript: ts } = modules;
12
12
  const pluginModule = {
@@ -36,7 +36,14 @@ function createAsyncLanguageServicePlugin(extensions, scriptKind, create) {
36
36
  if (getScriptKind) {
37
37
  info.languageServiceHost.getScriptKind = fileName => {
38
38
  if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
39
- return scriptKind; // TODO: bypass upstream bug
39
+ // bypass upstream bug https://github.com/microsoft/TypeScript/issues/57631
40
+ // TODO: check if the bug is fixed in 5.5
41
+ if (typeof getScriptKindForExtraExtensions === 'function') {
42
+ return getScriptKindForExtraExtensions(fileName);
43
+ }
44
+ else {
45
+ return getScriptKindForExtraExtensions;
46
+ }
40
47
  }
41
48
  return getScriptKind(fileName);
42
49
  };
@@ -52,17 +59,16 @@ function createAsyncLanguageServicePlugin(extensions, scriptKind, create) {
52
59
  const { proxy, initialize } = (0, proxyLanguageService_1.createProxyLanguageService)(info.languageService);
53
60
  info.languageService = proxy;
54
61
  create(ts, info).then(({ languagePlugins, setup }) => {
55
- const syncedScriptVersions = new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames);
56
62
  const language = (0, language_core_1.createLanguage)([
57
63
  ...languagePlugins,
58
64
  { getLanguageId: common_1.resolveFileLanguageId },
59
65
  ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
60
- const version = getScriptVersion(fileName);
61
- if (syncedScriptVersions.get(fileName) === version) {
62
- return;
66
+ let snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
67
+ if (!snapshot) {
68
+ // trigger projectService.getOrCreateScriptInfoNotOpenedByClient
69
+ info.project.getScriptVersion(fileName);
70
+ snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
63
71
  }
64
- syncedScriptVersions.set(fileName, version);
65
- const snapshot = getScriptSnapshot(fileName);
66
72
  if (snapshot) {
67
73
  language.scripts.set(fileName, snapshot);
68
74
  }
@@ -25,19 +25,16 @@ function createLanguageServicePlugin(create) {
25
25
  .map(plugin => plugin.typescript?.extraFileExtensions.map(ext => '.' + ext.extension) ?? [])
26
26
  .flat();
27
27
  exports.projectExternalFileExtensions.set(info.project, extensions);
28
- const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
29
- const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
30
- const syncedScriptVersions = new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames);
31
28
  const language = (0, language_core_1.createLanguage)([
32
29
  ...languagePlugins,
33
30
  { getLanguageId: common_1.resolveFileLanguageId },
34
31
  ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
35
- const version = getScriptVersion(fileName);
36
- if (syncedScriptVersions.get(fileName) === version) {
37
- return;
32
+ let snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
33
+ if (!snapshot) {
34
+ // trigger projectService.getOrCreateScriptInfoNotOpenedByClient
35
+ info.project.getScriptVersion(fileName);
36
+ snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
38
37
  }
39
- syncedScriptVersions.set(fileName, version);
40
- const snapshot = getScriptSnapshot(fileName);
41
38
  if (snapshot) {
42
39
  language.scripts.set(fileName, snapshot);
43
40
  }
@@ -4,4 +4,18 @@ export declare let getLanguagePlugins: (ts: typeof import('typescript'), options
4
4
  languagePlugins: LanguagePlugin<string>[];
5
5
  setup?(language: Language<string>): void;
6
6
  };
7
- export declare function runTsc(tscPath: string, extensions: string[], _getLanguagePlugins: typeof getLanguagePlugins): void;
7
+ export declare function runTsc(tscPath: string, options: string[] | {
8
+ extraSupportedExtensions: string[];
9
+ extraExtensionsToRemove: string[];
10
+ }, _getLanguagePlugins: typeof getLanguagePlugins): void;
11
+ /**
12
+ * Replaces the code of typescript to add support for additional extensions and language plugins.
13
+ *
14
+ * @param tsc - The original code of typescript.
15
+ * @param proxyApiPath - The path to the proxy API.
16
+ * @param extraSupportedExtensions - An array of additional supported extensions.
17
+ * @param extraExtensionsToRemove - An array of extensions to remove.
18
+ * @param getLanguagePluginsFile - The file to get language plugins from.
19
+ * @returns The modified typescript code.
20
+ */
21
+ export declare function transformTscContent(tsc: string, proxyApiPath: string, extraSupportedExtensions: string[], extraExtensionsToRemove: string[], getLanguagePluginsFile?: string): string;
@@ -2,31 +2,28 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getLanguagePlugins = void 0;
4
4
  exports.runTsc = runTsc;
5
+ exports.transformTscContent = transformTscContent;
5
6
  const fs = require("fs");
6
7
  let getLanguagePlugins = () => [];
7
8
  exports.getLanguagePlugins = getLanguagePlugins;
8
- function runTsc(tscPath, extensions, _getLanguagePlugins) {
9
+ function runTsc(tscPath, options, _getLanguagePlugins) {
9
10
  exports.getLanguagePlugins = _getLanguagePlugins;
10
11
  const proxyApiPath = require.resolve('../node/proxyCreateProgram');
11
12
  const readFileSync = fs.readFileSync;
12
13
  fs.readFileSync = (...args) => {
13
14
  if (args[0] === tscPath) {
14
15
  let tsc = readFileSync(...args);
15
- // add allow extensions
16
- const extsText = extensions.map(ext => `"${ext}"`).join(', ');
17
- tsc = replace(tsc, /supportedTSExtensions = .*(?=;)/, s => s + `.concat([[${extsText}]])`);
18
- tsc = replace(tsc, /supportedJSExtensions = .*(?=;)/, s => s + `.concat([[${extsText}]])`);
19
- tsc = replace(tsc, /allSupportedExtensions = .*(?=;)/, s => s + `.concat([[${extsText}]])`);
20
- // proxy createProgram
21
- tsc = replace(tsc, /function createProgram\(.+\) {/, s => `var createProgram = require(${JSON.stringify(proxyApiPath)}).proxyCreateProgram(`
22
- + [
23
- `new Proxy({}, { get(_target, p, _receiver) { return eval(p); } } )`,
24
- `_createProgram`,
25
- `require(${JSON.stringify(__filename)}).getLanguagePlugins`,
26
- ].join(', ')
27
- + `);\n`
28
- + s.replace('createProgram', '_createProgram'));
29
- return tsc;
16
+ let extraSupportedExtensions;
17
+ let extraExtensionsToRemove;
18
+ if (Array.isArray(options)) {
19
+ extraSupportedExtensions = options;
20
+ extraExtensionsToRemove = [];
21
+ }
22
+ else {
23
+ extraSupportedExtensions = options.extraSupportedExtensions;
24
+ extraExtensionsToRemove = options.extraExtensionsToRemove;
25
+ }
26
+ return transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove);
30
27
  }
31
28
  return readFileSync(...args);
32
29
  };
@@ -38,6 +35,50 @@ function runTsc(tscPath, extensions, _getLanguagePlugins) {
38
35
  delete require.cache[tscPath];
39
36
  }
40
37
  }
38
+ /**
39
+ * Replaces the code of typescript to add support for additional extensions and language plugins.
40
+ *
41
+ * @param tsc - The original code of typescript.
42
+ * @param proxyApiPath - The path to the proxy API.
43
+ * @param extraSupportedExtensions - An array of additional supported extensions.
44
+ * @param extraExtensionsToRemove - An array of extensions to remove.
45
+ * @param getLanguagePluginsFile - The file to get language plugins from.
46
+ * @returns The modified typescript code.
47
+ */
48
+ function transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, getLanguagePluginsFile = __filename) {
49
+ const neededPatchExtenstions = extraSupportedExtensions.filter(ext => !extraExtensionsToRemove.includes(ext));
50
+ // Add allow extensions
51
+ if (extraSupportedExtensions.length) {
52
+ const extsText = extraSupportedExtensions.map(ext => `"${ext}"`).join(', ');
53
+ tsc = replace(tsc, /supportedTSExtensions = .*(?=;)/, s => s + `.map((group, i) => i === 0 ? group.splice(0, 0, ${extsText}) && group : group)`);
54
+ tsc = replace(tsc, /supportedJSExtensions = .*(?=;)/, s => s + `.map((group, i) => i === 0 ? group.splice(0, 0, ${extsText}) && group : group)`);
55
+ tsc = replace(tsc, /allSupportedExtensions = .*(?=;)/, s => s + `.map((group, i) => i === 0 ? group.splice(0, 0, ${extsText}) && group : group)`);
56
+ }
57
+ // Use to emit basename.xxx to basename.d.ts instead of basename.xxx.d.ts
58
+ if (extraExtensionsToRemove.length) {
59
+ const extsText = extraExtensionsToRemove.map(ext => `"${ext}"`).join(', ');
60
+ tsc = replace(tsc, /extensionsToRemove = .*(?=;)/, s => s + `.concat([${extsText}])`);
61
+ }
62
+ // Support for basename.xxx to basename.xxx.d.ts
63
+ if (neededPatchExtenstions.length) {
64
+ const extsText = neededPatchExtenstions.map(ext => `"${ext}"`).join(', ');
65
+ tsc = replace(tsc, /function changeExtension\(/, s => `function changeExtension(path, newExtension) {
66
+ return [${extsText}].some(ext => path.endsWith(ext))
67
+ ? path + newExtension
68
+ : _changeExtension(path, newExtension)
69
+ }\n` + s.replace('changeExtension', '_changeExtension'));
70
+ }
71
+ // proxy createProgram
72
+ tsc = replace(tsc, /function createProgram\(.+\) {/, s => `var createProgram = require(${JSON.stringify(proxyApiPath)}).proxyCreateProgram(`
73
+ + [
74
+ `new Proxy({}, { get(_target, p, _receiver) { return eval(p); } } )`,
75
+ `_createProgram`,
76
+ `require(${JSON.stringify(getLanguagePluginsFile)}).getLanguagePlugins`,
77
+ ].join(', ')
78
+ + `);\n`
79
+ + s.replace('createProgram', '_createProgram'));
80
+ return tsc;
81
+ }
41
82
  function replace(text, ...[search, replace]) {
42
83
  const before = text;
43
84
  text = text.replace(search, replace);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volar/typescript",
3
- "version": "2.4.0-alpha.9",
3
+ "version": "2.4.1",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "**/*.js",
@@ -12,14 +12,14 @@
12
12
  "directory": "packages/typescript"
13
13
  },
14
14
  "dependencies": {
15
- "@volar/language-core": "2.4.0-alpha.9",
15
+ "@volar/language-core": "2.4.1",
16
16
  "path-browserify": "^1.0.1",
17
17
  "vscode-uri": "^3.0.8"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/node": "latest",
21
21
  "@types/path-browserify": "latest",
22
- "@volar/language-service": "2.4.0-alpha.9"
22
+ "@volar/language-service": "2.4.1"
23
23
  },
24
- "gitHead": "4a3fc1bf36eb12ccd58bc8cb25f11f1a9f7b2300"
24
+ "gitHead": "b920b6c4a3e4b2d8f46c676ea414e94c437cb5b7"
25
25
  }