@volar/typescript 2.4.10 → 2.4.12
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/node/decorateLanguageServiceHost.js +4 -4
- package/lib/node/proxyLanguageService.d.ts +12 -0
- package/lib/node/proxyLanguageService.js +12 -0
- package/lib/node/transform.d.ts +6 -0
- package/lib/node/transform.js +6 -0
- package/lib/quickstart/createAsyncLanguageServicePlugin.d.ts +21 -5
- package/lib/quickstart/createAsyncLanguageServicePlugin.js +85 -87
- package/lib/quickstart/createLanguageServicePlugin.d.ts +8 -10
- package/lib/quickstart/createLanguageServicePlugin.js +16 -65
- package/lib/quickstart/languageServicePluginCommon.d.ts +24 -0
- package/lib/quickstart/languageServicePluginCommon.js +109 -0
- package/lib/quickstart/runTsc.d.ts +3 -2
- package/lib/quickstart/runTsc.js +29 -13
- package/package.json +4 -4
|
@@ -55,7 +55,7 @@ function decorateLanguageServiceHost(ts, language, languageServiceHost) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
languageServiceHost.getScriptSnapshot = fileName => {
|
|
58
|
-
const virtualScript = updateVirtualScript(fileName);
|
|
58
|
+
const virtualScript = updateVirtualScript(fileName, true);
|
|
59
59
|
if (virtualScript) {
|
|
60
60
|
return virtualScript.snapshot;
|
|
61
61
|
}
|
|
@@ -63,14 +63,14 @@ function decorateLanguageServiceHost(ts, language, languageServiceHost) {
|
|
|
63
63
|
};
|
|
64
64
|
if (getScriptKind) {
|
|
65
65
|
languageServiceHost.getScriptKind = fileName => {
|
|
66
|
-
const virtualScript = updateVirtualScript(fileName);
|
|
66
|
+
const virtualScript = updateVirtualScript(fileName, false);
|
|
67
67
|
if (virtualScript) {
|
|
68
68
|
return virtualScript.scriptKind;
|
|
69
69
|
}
|
|
70
70
|
return getScriptKind(fileName);
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
-
function updateVirtualScript(fileName) {
|
|
73
|
+
function updateVirtualScript(fileName, shouldRegister) {
|
|
74
74
|
if (crashFileNames.has(fileName)) {
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
@@ -89,7 +89,7 @@ function decorateLanguageServiceHost(ts, language, languageServiceHost) {
|
|
|
89
89
|
let script = scripts.get(fileName);
|
|
90
90
|
if (!script || script[0] !== version) {
|
|
91
91
|
script = [version];
|
|
92
|
-
const sourceScript = language.scripts.get(fileName);
|
|
92
|
+
const sourceScript = language.scripts.get(fileName, undefined, shouldRegister);
|
|
93
93
|
if (sourceScript?.generated) {
|
|
94
94
|
const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
|
|
95
95
|
if (serviceScript) {
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { Language } from '@volar/language-core';
|
|
2
2
|
import type * as ts from 'typescript';
|
|
3
|
+
/**
|
|
4
|
+
* Creates and returns a Proxy around the base TypeScript LanguageService.
|
|
5
|
+
*
|
|
6
|
+
* This is used by the Volar TypeScript Plugin (which can be created by `createLanguageServicePlugin`
|
|
7
|
+
* and `createAsyncLanguageServicePlugin`) as an adapter layer between the TypeScript Language Service
|
|
8
|
+
* plugin API (see https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)
|
|
9
|
+
* and a Volar `Language`.
|
|
10
|
+
*
|
|
11
|
+
* Once the `initialize` method is called, the proxy will begin intercepting requests and
|
|
12
|
+
* enhancing the default behavior of the LanguageService with enhancements based on
|
|
13
|
+
* the Volar `Language` that has been passed to `initialize`.
|
|
14
|
+
*/
|
|
3
15
|
export declare function createProxyLanguageService(languageService: ts.LanguageService): {
|
|
4
16
|
initialize(language: Language<string>): void;
|
|
5
17
|
proxy: ts.LanguageService;
|
|
@@ -6,6 +6,18 @@ const dedupe_1 = require("./dedupe");
|
|
|
6
6
|
const transform_1 = require("./transform");
|
|
7
7
|
const utils_1 = require("./utils");
|
|
8
8
|
const windowsPathReg = /\\/g;
|
|
9
|
+
/**
|
|
10
|
+
* Creates and returns a Proxy around the base TypeScript LanguageService.
|
|
11
|
+
*
|
|
12
|
+
* This is used by the Volar TypeScript Plugin (which can be created by `createLanguageServicePlugin`
|
|
13
|
+
* and `createAsyncLanguageServicePlugin`) as an adapter layer between the TypeScript Language Service
|
|
14
|
+
* plugin API (see https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)
|
|
15
|
+
* and a Volar `Language`.
|
|
16
|
+
*
|
|
17
|
+
* Once the `initialize` method is called, the proxy will begin intercepting requests and
|
|
18
|
+
* enhancing the default behavior of the LanguageService with enhancements based on
|
|
19
|
+
* the Volar `Language` that has been passed to `initialize`.
|
|
20
|
+
*/
|
|
9
21
|
function createProxyLanguageService(languageService) {
|
|
10
22
|
const proxyCache = new Map();
|
|
11
23
|
let getProxyMethod;
|
package/lib/node/transform.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { CodeInformation, SourceScript } from '@volar/language-core';
|
|
|
2
2
|
import { Language } from '@volar/language-core';
|
|
3
3
|
import type * as ts from 'typescript';
|
|
4
4
|
import type { TypeScriptServiceScript } from '../..';
|
|
5
|
+
/**
|
|
6
|
+
* This file contains a number of facilities for transforming `ts.Diagnostic`s returned
|
|
7
|
+
* from the base TypeScript LanguageService, which reference locations in generated
|
|
8
|
+
* TS code (e.g. the TypeScript codegen'd from the script portion of a .vue file) into locations
|
|
9
|
+
* in the script portion of the .vue file.
|
|
10
|
+
*/
|
|
5
11
|
export declare function transformCallHierarchyItem(language: Language<string>, item: ts.CallHierarchyItem, fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean): ts.CallHierarchyItem;
|
|
6
12
|
export declare function transformDiagnostic<T extends ts.Diagnostic>(language: Language<string>, diagnostic: T, program: ts.Program | undefined, isTsc: boolean): T | undefined;
|
|
7
13
|
export declare function fillSourceFileText(language: Language<string>, sourceFile: ts.SourceFile): void;
|
package/lib/node/transform.js
CHANGED
|
@@ -19,6 +19,12 @@ const language_core_1 = require("@volar/language-core");
|
|
|
19
19
|
const utils_1 = require("./utils");
|
|
20
20
|
const transformedDiagnostics = new WeakMap();
|
|
21
21
|
const transformedSourceFile = new WeakSet();
|
|
22
|
+
/**
|
|
23
|
+
* This file contains a number of facilities for transforming `ts.Diagnostic`s returned
|
|
24
|
+
* from the base TypeScript LanguageService, which reference locations in generated
|
|
25
|
+
* TS code (e.g. the TypeScript codegen'd from the script portion of a .vue file) into locations
|
|
26
|
+
* in the script portion of the .vue file.
|
|
27
|
+
*/
|
|
22
28
|
function transformCallHierarchyItem(language, item, fallbackToAnyMatch, filter) {
|
|
23
29
|
const span = transformSpan(language, item.file, item.span, fallbackToAnyMatch, filter);
|
|
24
30
|
const selectionSpan = transformSpan(language, item.file, item.selectionSpan, fallbackToAnyMatch, filter);
|
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
import { Language, LanguagePlugin } from '@volar/language-core';
|
|
2
1
|
import type * as ts from 'typescript';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import type { createPluginCallbackAsync } from './languageServicePluginCommon';
|
|
3
|
+
/**
|
|
4
|
+
* Creates and returns a TS Service Plugin that supports async initialization.
|
|
5
|
+
* Essentially, this functions the same as `createLanguageServicePlugin`, but supports
|
|
6
|
+
* use cases in which the plugin callback must be async. For example in mdx-analyzer
|
|
7
|
+
* and Glint, this async variant is required because Glint + mdx-analyzer are written
|
|
8
|
+
* in ESM and get transpiled to CJS, which requires usage of `await import()` to load
|
|
9
|
+
* the necessary dependencies and fully initialize the plugin.
|
|
10
|
+
*
|
|
11
|
+
* To handle the period of time in which the plugin is initializing, this async
|
|
12
|
+
* variant stubs a number of methods on the LanguageServiceHost to handle the uninitialized state.
|
|
13
|
+
*
|
|
14
|
+
* Additionally, this async variant requires a few extra args pertaining to
|
|
15
|
+
* file extensions intended to be handled by the TS Plugin. In the synchronous variant,
|
|
16
|
+
* these can be synchronously inferred from elsewhere but for the async variant, they
|
|
17
|
+
* need to be passed in.
|
|
18
|
+
*
|
|
19
|
+
* See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
|
|
20
|
+
* more information.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createAsyncLanguageServicePlugin(extensions: string[], getScriptKindForExtraExtensions: ts.ScriptKind | ((fileName: string) => ts.ScriptKind), createPluginCallbackAsync: createPluginCallbackAsync): ts.server.PluginModuleFactory;
|
|
@@ -1,109 +1,107 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createAsyncLanguageServicePlugin = createAsyncLanguageServicePlugin;
|
|
4
|
-
const language_core_1 = require("@volar/language-core");
|
|
5
|
-
const common_1 = require("../common");
|
|
6
4
|
const proxyLanguageService_1 = require("../node/proxyLanguageService");
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const languageServicePluginCommon_1 = require("./languageServicePluginCommon");
|
|
6
|
+
/**
|
|
7
|
+
* Creates and returns a TS Service Plugin that supports async initialization.
|
|
8
|
+
* Essentially, this functions the same as `createLanguageServicePlugin`, but supports
|
|
9
|
+
* use cases in which the plugin callback must be async. For example in mdx-analyzer
|
|
10
|
+
* and Glint, this async variant is required because Glint + mdx-analyzer are written
|
|
11
|
+
* in ESM and get transpiled to CJS, which requires usage of `await import()` to load
|
|
12
|
+
* the necessary dependencies and fully initialize the plugin.
|
|
13
|
+
*
|
|
14
|
+
* To handle the period of time in which the plugin is initializing, this async
|
|
15
|
+
* variant stubs a number of methods on the LanguageServiceHost to handle the uninitialized state.
|
|
16
|
+
*
|
|
17
|
+
* Additionally, this async variant requires a few extra args pertaining to
|
|
18
|
+
* file extensions intended to be handled by the TS Plugin. In the synchronous variant,
|
|
19
|
+
* these can be synchronously inferred from elsewhere but for the async variant, they
|
|
20
|
+
* need to be passed in.
|
|
21
|
+
*
|
|
22
|
+
* See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
|
|
23
|
+
* more information.
|
|
24
|
+
*/
|
|
25
|
+
function createAsyncLanguageServicePlugin(extensions, getScriptKindForExtraExtensions, createPluginCallbackAsync) {
|
|
10
26
|
return modules => {
|
|
11
27
|
const { typescript: ts } = modules;
|
|
12
28
|
const pluginModule = {
|
|
13
29
|
create(info) {
|
|
14
|
-
if (!
|
|
15
|
-
|
|
16
|
-
createLanguageServicePlugin_1.decoratedLanguageServices.add(info.languageService);
|
|
17
|
-
createLanguageServicePlugin_1.decoratedLanguageServiceHosts.add(info.languageServiceHost);
|
|
18
|
-
const emptySnapshot = ts.ScriptSnapshot.fromString('');
|
|
19
|
-
const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
20
|
-
const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
|
|
21
|
-
const getScriptKind = info.languageServiceHost.getScriptKind?.bind(info.languageServiceHost);
|
|
22
|
-
const getProjectVersion = info.languageServiceHost.getProjectVersion?.bind(info.languageServiceHost);
|
|
23
|
-
let initialized = false;
|
|
24
|
-
info.languageServiceHost.getScriptSnapshot = fileName => {
|
|
25
|
-
if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
26
|
-
return emptySnapshot;
|
|
27
|
-
}
|
|
28
|
-
return getScriptSnapshot(fileName);
|
|
29
|
-
};
|
|
30
|
-
info.languageServiceHost.getScriptVersion = fileName => {
|
|
31
|
-
if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
32
|
-
return 'initializing...';
|
|
33
|
-
}
|
|
34
|
-
return getScriptVersion(fileName);
|
|
35
|
-
};
|
|
36
|
-
if (getScriptKind) {
|
|
37
|
-
info.languageServiceHost.getScriptKind = fileName => {
|
|
38
|
-
if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
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
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return getScriptKind(fileName);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
if (getProjectVersion) {
|
|
52
|
-
info.languageServiceHost.getProjectVersion = () => {
|
|
53
|
-
if (!initialized) {
|
|
54
|
-
return getProjectVersion() + ',initializing...';
|
|
55
|
-
}
|
|
56
|
-
return getProjectVersion();
|
|
57
|
-
};
|
|
58
|
-
}
|
|
30
|
+
if (!(0, languageServicePluginCommon_1.isHasAlreadyDecoratedLanguageService)(info)) {
|
|
31
|
+
const state = decorateWithAsyncInitializationHandling(ts, info, extensions, getScriptKindForExtraExtensions);
|
|
59
32
|
const { proxy, initialize } = (0, proxyLanguageService_1.createProxyLanguageService)(info.languageService);
|
|
60
33
|
info.languageService = proxy;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{ getLanguageId: common_1.resolveFileLanguageId },
|
|
65
|
-
], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
|
|
66
|
-
try { // getSnapshot could be crashed if the file is too large
|
|
67
|
-
let snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
|
|
68
|
-
if (!snapshot) {
|
|
69
|
-
// trigger projectService.getOrCreateScriptInfoNotOpenedByClient
|
|
70
|
-
info.project.getScriptVersion(fileName);
|
|
71
|
-
snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
|
|
72
|
-
}
|
|
73
|
-
if (snapshot) {
|
|
74
|
-
language.scripts.set(fileName, snapshot);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
language.scripts.delete(fileName);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch { }
|
|
81
|
-
});
|
|
82
|
-
initialize(language);
|
|
83
|
-
(0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
|
|
84
|
-
setup?.(language);
|
|
34
|
+
createPluginCallbackAsync(ts, info).then(createPluginResult => {
|
|
35
|
+
(0, languageServicePluginCommon_1.createLanguageCommon)(createPluginResult, ts, info, initialize);
|
|
36
|
+
state.initialized = true;
|
|
85
37
|
if ('markAsDirty' in info.project && typeof info.project.markAsDirty === 'function') {
|
|
38
|
+
// This is an attempt to mark the project as dirty so that in case the IDE/tsserver
|
|
39
|
+
// already finished a first pass of generating diagnostics (or other things), another
|
|
40
|
+
// pass will be triggered which should hopefully make use of this now-initialized plugin.
|
|
86
41
|
info.project.markAsDirty();
|
|
87
42
|
}
|
|
88
|
-
initialized = true;
|
|
89
43
|
});
|
|
90
44
|
}
|
|
91
45
|
return info.languageService;
|
|
92
46
|
},
|
|
93
|
-
getExternalFiles(
|
|
94
|
-
if (updateLevel >= 1
|
|
95
|
-
|| !createLanguageServicePlugin_1.externalFiles.has(project)) {
|
|
96
|
-
const oldFiles = createLanguageServicePlugin_1.externalFiles.get(project);
|
|
97
|
-
const newFiles = extensions.length ? (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions) : [];
|
|
98
|
-
createLanguageServicePlugin_1.externalFiles.set(project, newFiles);
|
|
99
|
-
if (oldFiles && !(0, createLanguageServicePlugin_1.arrayItemsEqual)(oldFiles, newFiles)) {
|
|
100
|
-
project.refreshDiagnostics();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return createLanguageServicePlugin_1.externalFiles.get(project);
|
|
104
|
-
},
|
|
47
|
+
getExternalFiles: (0, languageServicePluginCommon_1.makeGetExternalFiles)(ts),
|
|
105
48
|
};
|
|
106
49
|
return pluginModule;
|
|
107
50
|
};
|
|
108
51
|
}
|
|
52
|
+
function decorateWithAsyncInitializationHandling(ts, info, extensions, getScriptKindForExtraExtensions) {
|
|
53
|
+
const emptySnapshot = ts.ScriptSnapshot.fromString('');
|
|
54
|
+
const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
55
|
+
const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
|
|
56
|
+
const getScriptKind = info.languageServiceHost.getScriptKind?.bind(info.languageServiceHost);
|
|
57
|
+
const getProjectVersion = info.languageServiceHost.getProjectVersion?.bind(info.languageServiceHost);
|
|
58
|
+
const getScriptInfo = (0, languageServicePluginCommon_1.makeGetScriptInfoWithLargeFileFailsafe)(info);
|
|
59
|
+
const state = { initialized: false };
|
|
60
|
+
info.languageServiceHost.getScriptSnapshot = fileName => {
|
|
61
|
+
if (!state.initialized) {
|
|
62
|
+
if (extensions.some(ext => fileName.endsWith(ext))) {
|
|
63
|
+
return emptySnapshot;
|
|
64
|
+
}
|
|
65
|
+
if (getScriptInfo(fileName)?.isScriptOpen()) {
|
|
66
|
+
return emptySnapshot;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return getScriptSnapshot(fileName);
|
|
70
|
+
};
|
|
71
|
+
info.languageServiceHost.getScriptVersion = fileName => {
|
|
72
|
+
if (!state.initialized) {
|
|
73
|
+
if (extensions.some(ext => fileName.endsWith(ext))) {
|
|
74
|
+
return 'initializing...';
|
|
75
|
+
}
|
|
76
|
+
if (getScriptInfo(fileName)?.isScriptOpen()) {
|
|
77
|
+
return getScriptVersion(fileName) + ',initializing...';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return getScriptVersion(fileName);
|
|
81
|
+
};
|
|
82
|
+
if (getScriptKind) {
|
|
83
|
+
info.languageServiceHost.getScriptKind = fileName => {
|
|
84
|
+
if (!state.initialized && extensions.some(ext => fileName.endsWith(ext))) {
|
|
85
|
+
// bypass upstream bug https://github.com/microsoft/TypeScript/issues/57631
|
|
86
|
+
// TODO: check if the bug is fixed in 5.5
|
|
87
|
+
if (typeof getScriptKindForExtraExtensions === 'function') {
|
|
88
|
+
return getScriptKindForExtraExtensions(fileName);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return getScriptKindForExtraExtensions;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return getScriptKind(fileName);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (getProjectVersion) {
|
|
98
|
+
info.languageServiceHost.getProjectVersion = () => {
|
|
99
|
+
if (!state.initialized) {
|
|
100
|
+
return getProjectVersion() + ',initializing...';
|
|
101
|
+
}
|
|
102
|
+
return getProjectVersion();
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
109
107
|
//# sourceMappingURL=createAsyncLanguageServicePlugin.js.map
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { Language, LanguagePlugin } from '@volar/language-core';
|
|
2
1
|
import type * as ts from 'typescript';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export declare function arrayItemsEqual(a: string[], b: string[]): boolean;
|
|
2
|
+
import type { createPluginCallbackSync } from './languageServicePluginCommon';
|
|
3
|
+
/**
|
|
4
|
+
* Creates and returns a TS Service Plugin using Volar primitives.
|
|
5
|
+
*
|
|
6
|
+
* See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
|
|
7
|
+
* more information.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createLanguageServicePlugin(createPluginCallback: createPluginCallbackSync): ts.server.PluginModuleFactory;
|
|
@@ -1,85 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.decoratedLanguageServiceHosts = exports.decoratedLanguageServices = exports.projectExternalFileExtensions = exports.externalFiles = void 0;
|
|
4
3
|
exports.createLanguageServicePlugin = createLanguageServicePlugin;
|
|
5
|
-
exports.arrayItemsEqual = arrayItemsEqual;
|
|
6
|
-
const language_core_1 = require("@volar/language-core");
|
|
7
|
-
const common_1 = require("../common");
|
|
8
4
|
const proxyLanguageService_1 = require("../node/proxyLanguageService");
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
const languageServicePluginCommon_1 = require("./languageServicePluginCommon");
|
|
6
|
+
/**
|
|
7
|
+
* Creates and returns a TS Service Plugin using Volar primitives.
|
|
8
|
+
*
|
|
9
|
+
* See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
|
|
10
|
+
* more information.
|
|
11
|
+
*/
|
|
12
|
+
function createLanguageServicePlugin(createPluginCallback) {
|
|
15
13
|
return modules => {
|
|
16
14
|
const { typescript: ts } = modules;
|
|
17
15
|
const pluginModule = {
|
|
18
16
|
create(info) {
|
|
19
|
-
if (!
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
exports.decoratedLanguageServiceHosts.add(info.languageServiceHost);
|
|
23
|
-
const { languagePlugins, setup } = create(ts, info);
|
|
24
|
-
const extensions = languagePlugins
|
|
17
|
+
if (!(0, languageServicePluginCommon_1.isHasAlreadyDecoratedLanguageService)(info)) {
|
|
18
|
+
const createPluginResult = createPluginCallback(ts, info);
|
|
19
|
+
const extensions = createPluginResult.languagePlugins
|
|
25
20
|
.map(plugin => plugin.typescript?.extraFileExtensions.map(ext => '.' + ext.extension) ?? [])
|
|
26
21
|
.flat();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{ getLanguageId: common_1.resolveFileLanguageId },
|
|
31
|
-
], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
|
|
32
|
-
try { // getSnapshot could be crashed if the file is too large
|
|
33
|
-
let snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
|
|
34
|
-
if (!snapshot) {
|
|
35
|
-
// trigger projectService.getOrCreateScriptInfoNotOpenedByClient
|
|
36
|
-
info.project.getScriptVersion(fileName);
|
|
37
|
-
snapshot = info.project.getScriptInfo(fileName)?.getSnapshot();
|
|
38
|
-
}
|
|
39
|
-
if (snapshot) {
|
|
40
|
-
language.scripts.set(fileName, snapshot);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
language.scripts.delete(fileName);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch { }
|
|
47
|
-
});
|
|
22
|
+
// TODO: this logic does not seem to appear in the async variant
|
|
23
|
+
// (createAsyncLanguageServicePlugin)... bug?
|
|
24
|
+
languageServicePluginCommon_1.projectExternalFileExtensions.set(info.project, extensions);
|
|
48
25
|
const { proxy, initialize } = (0, proxyLanguageService_1.createProxyLanguageService)(info.languageService);
|
|
49
26
|
info.languageService = proxy;
|
|
50
|
-
|
|
51
|
-
(0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
|
|
52
|
-
setup?.(language);
|
|
27
|
+
(0, languageServicePluginCommon_1.createLanguageCommon)(createPluginResult, ts, info, initialize);
|
|
53
28
|
}
|
|
54
29
|
return info.languageService;
|
|
55
30
|
},
|
|
56
|
-
getExternalFiles(
|
|
57
|
-
if (updateLevel >= 1
|
|
58
|
-
|| !exports.externalFiles.has(project)) {
|
|
59
|
-
const oldFiles = exports.externalFiles.get(project);
|
|
60
|
-
const extensions = exports.projectExternalFileExtensions.get(project);
|
|
61
|
-
const newFiles = extensions?.length ? (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions) : [];
|
|
62
|
-
exports.externalFiles.set(project, newFiles);
|
|
63
|
-
if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) {
|
|
64
|
-
project.refreshDiagnostics();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return exports.externalFiles.get(project);
|
|
68
|
-
},
|
|
31
|
+
getExternalFiles: (0, languageServicePluginCommon_1.makeGetExternalFiles)(ts),
|
|
69
32
|
};
|
|
70
33
|
return pluginModule;
|
|
71
34
|
};
|
|
72
35
|
}
|
|
73
|
-
function arrayItemsEqual(a, b) {
|
|
74
|
-
if (a.length !== b.length) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
const set = new Set(a);
|
|
78
|
-
for (const file of b) {
|
|
79
|
-
if (!set.has(file)) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
36
|
//# sourceMappingURL=createLanguageServicePlugin.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Language, LanguagePlugin } from '@volar/language-core/lib/types';
|
|
2
|
+
import type * as ts from 'typescript';
|
|
3
|
+
export declare const externalFiles: WeakMap<ts.server.Project, string[]>;
|
|
4
|
+
export declare const projectExternalFileExtensions: WeakMap<ts.server.Project, string[]>;
|
|
5
|
+
export declare const decoratedLanguageServices: WeakSet<ts.LanguageService>;
|
|
6
|
+
export declare const decoratedLanguageServiceHosts: WeakSet<ts.LanguageServiceHost>;
|
|
7
|
+
/**
|
|
8
|
+
* Wrap `getScriptInfo` to handle large files that may crash the language service.
|
|
9
|
+
*
|
|
10
|
+
* Introduced to fix issues with converting `relatedInformation` (in Diagnostics)
|
|
11
|
+
* when working with large files.
|
|
12
|
+
*
|
|
13
|
+
* https://github.com/volarjs/volar.js/commit/e242709a91e9d2919dc4fa59278dd266fd11e7a3
|
|
14
|
+
*/
|
|
15
|
+
export declare function makeGetScriptInfoWithLargeFileFailsafe(info: ts.server.PluginCreateInfo): (fileName: string) => ts.server.ScriptInfo | undefined;
|
|
16
|
+
export declare function createLanguageCommon(createPluginResult: createPluginCallbackReturnValue, ts: typeof import('typescript'), info: ts.server.PluginCreateInfo, initializeProxiedLanguageService: (language: Language<string>) => void): void;
|
|
17
|
+
export declare const makeGetExternalFiles: (ts: typeof import("typescript")) => (project: ts.server.Project, updateLevel?: number) => string[];
|
|
18
|
+
export type createPluginCallbackReturnValue = {
|
|
19
|
+
languagePlugins: LanguagePlugin<string>[];
|
|
20
|
+
setup?: (language: Language<string>) => void;
|
|
21
|
+
};
|
|
22
|
+
export type createPluginCallbackSync = (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => createPluginCallbackReturnValue;
|
|
23
|
+
export type createPluginCallbackAsync = (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => Promise<createPluginCallbackReturnValue>;
|
|
24
|
+
export declare function isHasAlreadyDecoratedLanguageService(info: ts.server.PluginCreateInfo): boolean;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeGetExternalFiles = exports.decoratedLanguageServiceHosts = exports.decoratedLanguageServices = exports.projectExternalFileExtensions = exports.externalFiles = void 0;
|
|
4
|
+
exports.makeGetScriptInfoWithLargeFileFailsafe = makeGetScriptInfoWithLargeFileFailsafe;
|
|
5
|
+
exports.createLanguageCommon = createLanguageCommon;
|
|
6
|
+
exports.isHasAlreadyDecoratedLanguageService = isHasAlreadyDecoratedLanguageService;
|
|
7
|
+
const language_core_1 = require("@volar/language-core");
|
|
8
|
+
const common_1 = require("../common");
|
|
9
|
+
const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
|
|
10
|
+
exports.externalFiles = new WeakMap();
|
|
11
|
+
exports.projectExternalFileExtensions = new WeakMap();
|
|
12
|
+
exports.decoratedLanguageServices = new WeakSet();
|
|
13
|
+
exports.decoratedLanguageServiceHosts = new WeakSet();
|
|
14
|
+
/**
|
|
15
|
+
* Wrap `getScriptInfo` to handle large files that may crash the language service.
|
|
16
|
+
*
|
|
17
|
+
* Introduced to fix issues with converting `relatedInformation` (in Diagnostics)
|
|
18
|
+
* when working with large files.
|
|
19
|
+
*
|
|
20
|
+
* https://github.com/volarjs/volar.js/commit/e242709a91e9d2919dc4fa59278dd266fd11e7a3
|
|
21
|
+
*/
|
|
22
|
+
function makeGetScriptInfoWithLargeFileFailsafe(info) {
|
|
23
|
+
return (fileName) => {
|
|
24
|
+
// getSnapshot could be crashed if the file is too large
|
|
25
|
+
try {
|
|
26
|
+
return info.project.getScriptInfo(fileName);
|
|
27
|
+
}
|
|
28
|
+
catch { }
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function createLanguageCommon(createPluginResult, ts, info, initializeProxiedLanguageService) {
|
|
32
|
+
const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
33
|
+
const getScriptInfo = makeGetScriptInfoWithLargeFileFailsafe(info);
|
|
34
|
+
const language = (0, language_core_1.createLanguage)([
|
|
35
|
+
...createPluginResult.languagePlugins,
|
|
36
|
+
{ getLanguageId: common_1.resolveFileLanguageId },
|
|
37
|
+
], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), (fileName, _, shouldRegister) => {
|
|
38
|
+
let snapshot;
|
|
39
|
+
if (shouldRegister) {
|
|
40
|
+
// We need to trigger registration of the script file with the project, see #250
|
|
41
|
+
snapshot = getScriptSnapshot(fileName);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
snapshot = getScriptInfo(fileName)?.getSnapshot();
|
|
45
|
+
if (!snapshot) {
|
|
46
|
+
// trigger projectService.getOrCreateScriptInfoNotOpenedByClient
|
|
47
|
+
info.project.getScriptVersion(fileName);
|
|
48
|
+
snapshot = getScriptInfo(fileName)?.getSnapshot();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (snapshot) {
|
|
52
|
+
language.scripts.set(fileName, snapshot);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
language.scripts.delete(fileName);
|
|
56
|
+
}
|
|
57
|
+
}, targetFileName => {
|
|
58
|
+
// https://github.com/JetBrains/intellij-plugins/blob/6435723ad88fa296b41144162ebe3b8513f4949b/Angular/src-js/angular-service/src/ngCommands.ts#L88
|
|
59
|
+
info.session.change({
|
|
60
|
+
file: targetFileName,
|
|
61
|
+
line: 1,
|
|
62
|
+
offset: 1,
|
|
63
|
+
endLine: 1,
|
|
64
|
+
endOffset: 1,
|
|
65
|
+
insertString: '',
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
initializeProxiedLanguageService(language);
|
|
69
|
+
(0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
|
|
70
|
+
createPluginResult.setup?.(language);
|
|
71
|
+
}
|
|
72
|
+
const makeGetExternalFiles = (ts) => (project, updateLevel = 0) => {
|
|
73
|
+
if (updateLevel >= 1
|
|
74
|
+
|| !exports.externalFiles.has(project)) {
|
|
75
|
+
const oldFiles = exports.externalFiles.get(project);
|
|
76
|
+
const extensions = exports.projectExternalFileExtensions.get(project);
|
|
77
|
+
const newFiles = extensions?.length ? (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions) : [];
|
|
78
|
+
exports.externalFiles.set(project, newFiles);
|
|
79
|
+
if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) {
|
|
80
|
+
project.refreshDiagnostics();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return exports.externalFiles.get(project);
|
|
84
|
+
};
|
|
85
|
+
exports.makeGetExternalFiles = makeGetExternalFiles;
|
|
86
|
+
function arrayItemsEqual(a, b) {
|
|
87
|
+
if (a.length !== b.length) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const set = new Set(a);
|
|
91
|
+
for (const file of b) {
|
|
92
|
+
if (!set.has(file)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
function isHasAlreadyDecoratedLanguageService(info) {
|
|
99
|
+
if (exports.decoratedLanguageServices.has(info.languageService)
|
|
100
|
+
|| exports.decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
exports.decoratedLanguageServices.add(info.languageService);
|
|
105
|
+
exports.decoratedLanguageServiceHosts.add(info.languageServiceHost);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=languageServicePluginCommon.js.map
|
|
@@ -7,7 +7,7 @@ export declare let getLanguagePlugins: (ts: typeof import('typescript'), options
|
|
|
7
7
|
export declare function runTsc(tscPath: string, options: string[] | {
|
|
8
8
|
extraSupportedExtensions: string[];
|
|
9
9
|
extraExtensionsToRemove: string[];
|
|
10
|
-
}, _getLanguagePlugins: typeof getLanguagePlugins): void;
|
|
10
|
+
}, _getLanguagePlugins: typeof getLanguagePlugins, typescriptObject?: string): void;
|
|
11
11
|
/**
|
|
12
12
|
* Replaces the code of typescript to add support for additional extensions and language plugins.
|
|
13
13
|
*
|
|
@@ -16,6 +16,7 @@ export declare function runTsc(tscPath: string, options: string[] | {
|
|
|
16
16
|
* @param extraSupportedExtensions - An array of additional supported extensions.
|
|
17
17
|
* @param extraExtensionsToRemove - An array of extensions to remove.
|
|
18
18
|
* @param getLanguagePluginsFile - The file to get language plugins from.
|
|
19
|
+
* @param typescriptObject - The object to use as typescript.
|
|
19
20
|
* @returns The modified typescript code.
|
|
20
21
|
*/
|
|
21
|
-
export declare function transformTscContent(tsc: string, proxyApiPath: string, extraSupportedExtensions: string[], extraExtensionsToRemove: string[], getLanguagePluginsFile?: string): string;
|
|
22
|
+
export declare function transformTscContent(tsc: string, proxyApiPath: string, extraSupportedExtensions: string[], extraExtensionsToRemove: string[], getLanguagePluginsFile?: string, typescriptObject?: string): string;
|
package/lib/quickstart/runTsc.js
CHANGED
|
@@ -4,26 +4,41 @@ exports.getLanguagePlugins = void 0;
|
|
|
4
4
|
exports.runTsc = runTsc;
|
|
5
5
|
exports.transformTscContent = transformTscContent;
|
|
6
6
|
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
7
8
|
let getLanguagePlugins = () => [];
|
|
8
9
|
exports.getLanguagePlugins = getLanguagePlugins;
|
|
9
|
-
function runTsc(tscPath, options, _getLanguagePlugins) {
|
|
10
|
+
function runTsc(tscPath, options, _getLanguagePlugins, typescriptObject) {
|
|
10
11
|
exports.getLanguagePlugins = _getLanguagePlugins;
|
|
12
|
+
let extraSupportedExtensions;
|
|
13
|
+
let extraExtensionsToRemove;
|
|
14
|
+
if (Array.isArray(options)) {
|
|
15
|
+
extraSupportedExtensions = options;
|
|
16
|
+
extraExtensionsToRemove = [];
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
extraSupportedExtensions = options.extraSupportedExtensions;
|
|
20
|
+
extraExtensionsToRemove = options.extraExtensionsToRemove;
|
|
21
|
+
}
|
|
11
22
|
const proxyApiPath = require.resolve('../node/proxyCreateProgram');
|
|
12
23
|
const readFileSync = fs.readFileSync;
|
|
13
24
|
fs.readFileSync = (...args) => {
|
|
14
25
|
if (args[0] === tscPath) {
|
|
15
26
|
let tsc = readFileSync(...args);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (Array.isArray(options)) {
|
|
19
|
-
extraSupportedExtensions = options;
|
|
20
|
-
extraExtensionsToRemove = [];
|
|
27
|
+
try {
|
|
28
|
+
return transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, __filename, typescriptObject);
|
|
21
29
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
catch {
|
|
31
|
+
// Support the tsc shim used in Typescript v5.7 and up
|
|
32
|
+
const requireRegex = /module\.exports\s*=\s*require\((?:"|')(?<path>\.\/\w+\.js)(?:"|')\)/;
|
|
33
|
+
const requirePath = requireRegex.exec(tsc)?.groups?.path;
|
|
34
|
+
if (requirePath) {
|
|
35
|
+
tsc = readFileSync(path.join(path.dirname(tscPath), requirePath), 'utf8');
|
|
36
|
+
return transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, __filename, typescriptObject);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
throw new Error('Failed to locate tsc module path from shim');
|
|
40
|
+
}
|
|
25
41
|
}
|
|
26
|
-
return transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove);
|
|
27
42
|
}
|
|
28
43
|
return readFileSync(...args);
|
|
29
44
|
};
|
|
@@ -43,9 +58,10 @@ function runTsc(tscPath, options, _getLanguagePlugins) {
|
|
|
43
58
|
* @param extraSupportedExtensions - An array of additional supported extensions.
|
|
44
59
|
* @param extraExtensionsToRemove - An array of extensions to remove.
|
|
45
60
|
* @param getLanguagePluginsFile - The file to get language plugins from.
|
|
61
|
+
* @param typescriptObject - The object to use as typescript.
|
|
46
62
|
* @returns The modified typescript code.
|
|
47
63
|
*/
|
|
48
|
-
function transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, getLanguagePluginsFile = __filename) {
|
|
64
|
+
function transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, getLanguagePluginsFile = __filename, typescriptObject = `new Proxy({}, { get(_target, p, _receiver) { return eval(p); } } )`) {
|
|
49
65
|
const neededPatchExtenstions = extraSupportedExtensions.filter(ext => !extraExtensionsToRemove.includes(ext));
|
|
50
66
|
// Add allow extensions
|
|
51
67
|
if (extraSupportedExtensions.length) {
|
|
@@ -71,7 +87,7 @@ function transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraE
|
|
|
71
87
|
// proxy createProgram
|
|
72
88
|
tsc = replace(tsc, /function createProgram\(.+\) {/, s => `var createProgram = require(${JSON.stringify(proxyApiPath)}).proxyCreateProgram(`
|
|
73
89
|
+ [
|
|
74
|
-
|
|
90
|
+
typescriptObject,
|
|
75
91
|
`_createProgram`,
|
|
76
92
|
`require(${JSON.stringify(getLanguagePluginsFile)}).getLanguagePlugins`,
|
|
77
93
|
].join(', ')
|
|
@@ -84,7 +100,7 @@ function replace(text, ...[search, replace]) {
|
|
|
84
100
|
text = text.replace(search, replace);
|
|
85
101
|
const after = text;
|
|
86
102
|
if (after === before) {
|
|
87
|
-
throw '
|
|
103
|
+
throw new Error('Failed to replace: ' + search);
|
|
88
104
|
}
|
|
89
105
|
return after;
|
|
90
106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volar/typescript",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.12",
|
|
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.
|
|
15
|
+
"@volar/language-core": "2.4.12",
|
|
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.
|
|
22
|
+
"@volar/language-service": "2.4.12"
|
|
23
23
|
},
|
|
24
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "17b9b8a1f522afd1aad1e598d2fd935680d8a8d7"
|
|
25
25
|
}
|