@volar/typescript 2.2.0-alpha.1 → 2.2.0-alpha.11

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.
@@ -1,3 +1,3 @@
1
- import type * as ts from 'typescript';
2
1
  import { LanguagePlugin } from '@volar/language-core';
3
- export declare function proxyCreateProgram(ts: typeof import('typescript'), original: typeof ts['createProgram'], extensions: string[], getLanguagePlugins: (ts: typeof import('typescript'), options: ts.CreateProgramOptions) => LanguagePlugin[]): typeof import("typescript").createProgram;
2
+ import type * as ts from 'typescript';
3
+ export declare function proxyCreateProgram(ts: typeof import('typescript'), original: typeof ts['createProgram'], getLanguagePlugins: (ts: typeof import('typescript'), options: ts.CreateProgramOptions) => LanguagePlugin[]): typeof import("typescript").createProgram;
@@ -1,117 +1,165 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.proxyCreateProgram = void 0;
4
- const decorateProgram_1 = require("./decorateProgram");
5
4
  const language_core_1 = require("@volar/language-core");
6
- function proxyCreateProgram(ts, original, extensions, getLanguagePlugins) {
5
+ const resolveModuleName_1 = require("../resolveModuleName");
6
+ const decorateProgram_1 = require("./decorateProgram");
7
+ const common_1 = require("../common");
8
+ const arrayEqual = (a, b) => {
9
+ if (a.length !== b.length) {
10
+ return false;
11
+ }
12
+ for (let i = 0; i < a.length; i++) {
13
+ if (a[i] !== b[i]) {
14
+ return false;
15
+ }
16
+ }
17
+ return true;
18
+ };
19
+ const objectEqual = (a, b) => {
20
+ const keysA = Object.keys(a);
21
+ const keysB = Object.keys(b);
22
+ if (keysA.length !== keysB.length) {
23
+ return false;
24
+ }
25
+ for (const key of keysA) {
26
+ if (a[key] !== b[key]) {
27
+ return false;
28
+ }
29
+ }
30
+ return true;
31
+ };
32
+ function proxyCreateProgram(ts, original, getLanguagePlugins) {
33
+ const sourceFileSnapshots = new Map();
34
+ const parsedSourceFiles = new WeakMap();
35
+ let lastOptions;
36
+ let languagePlugins;
37
+ let language;
38
+ let moduleResolutionCache;
7
39
  return new Proxy(original, {
8
40
  apply: (target, thisArg, args) => {
9
41
  const options = args[0];
10
42
  assert(!!options.host, '!!options.host');
11
- const sourceFileToSnapshotMap = new WeakMap();
12
- const language = (0, language_core_1.createLanguage)(getLanguagePlugins(ts, options), ts.sys.useCaseSensitiveFileNames, fileName => {
13
- let snapshot;
14
- assert(originalSourceFiles.has(fileName), `originalSourceFiles.has(${fileName})`);
15
- const sourceFile = originalSourceFiles.get(fileName);
16
- if (sourceFile) {
17
- snapshot = sourceFileToSnapshotMap.get(sourceFile);
18
- if (!snapshot) {
19
- snapshot = {
20
- getChangeRange() {
21
- return undefined;
22
- },
23
- getLength() {
24
- return sourceFile.text.length;
25
- },
26
- getText(start, end) {
27
- return sourceFile.text.substring(start, end);
28
- },
29
- };
30
- sourceFileToSnapshotMap.set(sourceFile, snapshot);
31
- }
32
- }
33
- if (snapshot) {
34
- language.scripts.set(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
35
- }
36
- else {
37
- language.scripts.delete(fileName);
38
- }
39
- });
40
- const originalSourceFiles = new Map();
41
- const parsedSourceFiles = new WeakMap();
42
- const arbitraryExtensions = extensions.map(ext => `.d${ext}.ts`);
43
- const originalHost = options.host;
44
- const moduleResolutionHost = {
45
- ...originalHost,
46
- fileExists(fileName) {
47
- for (let i = 0; i < arbitraryExtensions.length; i++) {
48
- if (fileName.endsWith(arbitraryExtensions[i])) {
49
- return originalHost.fileExists(fileName.slice(0, -arbitraryExtensions[i].length) + extensions[i]);
43
+ if (!lastOptions
44
+ || !languagePlugins
45
+ || !language
46
+ || !arrayEqual(options.rootNames, lastOptions.rootNames)
47
+ || !objectEqual(options.options, lastOptions.options)) {
48
+ moduleResolutionCache = ts.createModuleResolutionCache(options.host.getCurrentDirectory(), options.host.getCanonicalFileName, options.options);
49
+ lastOptions = options;
50
+ languagePlugins = getLanguagePlugins(ts, options);
51
+ language = (0, language_core_1.createLanguage)([
52
+ ...languagePlugins,
53
+ common_1.fileLanguageIdProviderPlugin,
54
+ ], ts.sys.useCaseSensitiveFileNames, fileName => {
55
+ if (!sourceFileSnapshots.has(fileName)) {
56
+ const sourceFileText = originalHost.readFile(fileName);
57
+ if (sourceFileText !== undefined) {
58
+ sourceFileSnapshots.set(fileName, [undefined, {
59
+ getChangeRange() {
60
+ return undefined;
61
+ },
62
+ getLength() {
63
+ return sourceFileText.length;
64
+ },
65
+ getText(start, end) {
66
+ return sourceFileText.substring(start, end);
67
+ },
68
+ }]);
69
+ }
70
+ else {
71
+ sourceFileSnapshots.set(fileName, [undefined, undefined]);
50
72
  }
51
73
  }
52
- return originalHost.fileExists(fileName);
53
- },
54
- };
74
+ const snapshot = sourceFileSnapshots.get(fileName)?.[1];
75
+ if (snapshot) {
76
+ language.scripts.set(fileName, snapshot);
77
+ }
78
+ else {
79
+ language.scripts.delete(fileName);
80
+ }
81
+ });
82
+ }
83
+ const originalHost = options.host;
84
+ const extensions = languagePlugins
85
+ .map(plugin => plugin.typescript?.extraFileExtensions.map(({ extension }) => `.${extension}`) ?? [])
86
+ .flat();
55
87
  options.host = { ...originalHost };
56
- options.options.allowArbitraryExtensions = true;
57
88
  options.host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
58
89
  const originalSourceFile = originalHost.getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
59
- originalSourceFiles.set(fileName, originalSourceFile);
60
- if (originalSourceFile && extensions.some(ext => fileName.endsWith(ext))) {
61
- let sourceFile2 = parsedSourceFiles.get(originalSourceFile);
62
- if (!sourceFile2) {
63
- const sourceScript = language.scripts.get(fileName);
64
- assert(!!sourceScript, '!!sourceScript');
65
- let patchedText = originalSourceFile.text.split('\n').map(line => ' '.repeat(line.length)).join('\n');
66
- let scriptKind = ts.ScriptKind.TS;
67
- if (sourceScript.generated?.languagePlugin.typescript) {
68
- const { getServiceScript: getScript, getExtraServiceScripts: getExtraScripts } = sourceScript.generated.languagePlugin.typescript;
69
- const serviceScript = getScript(sourceScript.generated.root);
70
- if (serviceScript) {
71
- scriptKind = serviceScript.scriptKind;
72
- patchedText += serviceScript.code.snapshot.getText(0, serviceScript.code.snapshot.getLength());
73
- }
74
- if (getExtraScripts) {
75
- console.warn('getExtraScripts() is not available in this use case.');
76
- }
90
+ if (!sourceFileSnapshots.has(fileName)
91
+ || sourceFileSnapshots.get(fileName)?.[0] !== originalSourceFile) {
92
+ if (originalSourceFile) {
93
+ sourceFileSnapshots.set(fileName, [originalSourceFile, {
94
+ getChangeRange() {
95
+ return undefined;
96
+ },
97
+ getLength() {
98
+ return originalSourceFile.text.length;
99
+ },
100
+ getText(start, end) {
101
+ return originalSourceFile.text.substring(start, end);
102
+ },
103
+ }]);
104
+ }
105
+ else {
106
+ sourceFileSnapshots.set(fileName, [undefined, undefined]);
107
+ }
108
+ }
109
+ if (!originalSourceFile) {
110
+ return;
111
+ }
112
+ if (!parsedSourceFiles.has(originalSourceFile)) {
113
+ const sourceScript = language.scripts.get(fileName);
114
+ assert(!!sourceScript, '!!sourceScript');
115
+ parsedSourceFiles.set(originalSourceFile, originalSourceFile);
116
+ if (sourceScript.generated?.languagePlugin.typescript) {
117
+ const { getServiceScript, getExtraServiceScripts } = sourceScript.generated.languagePlugin.typescript;
118
+ const serviceScript = getServiceScript(sourceScript.generated.root);
119
+ if (serviceScript) {
120
+ let patchedText = originalSourceFile.text.split('\n').map(line => ' '.repeat(line.length)).join('\n');
121
+ let scriptKind = ts.ScriptKind.TS;
122
+ scriptKind = serviceScript.scriptKind;
123
+ patchedText += serviceScript.code.snapshot.getText(0, serviceScript.code.snapshot.getLength());
124
+ const parsedSourceFile = ts.createSourceFile(fileName, patchedText, languageVersionOrOptions, undefined, scriptKind);
125
+ // @ts-expect-error
126
+ parsedSourceFile.version = originalSourceFile.version;
127
+ parsedSourceFiles.set(originalSourceFile, parsedSourceFile);
128
+ }
129
+ if (getExtraServiceScripts) {
130
+ console.warn('getExtraServiceScripts() is not available in this use case.');
77
131
  }
78
- sourceFile2 = ts.createSourceFile(fileName, patchedText, 99, true, scriptKind);
79
- // @ts-expect-error
80
- sourceFile2.version = originalSourceFile.version;
81
- parsedSourceFiles.set(originalSourceFile, sourceFile2);
82
132
  }
83
- return sourceFile2;
84
133
  }
85
- return originalSourceFile;
86
- };
87
- options.host.resolveModuleNameLiterals = (moduleNames, containingFile, redirectedReference, options) => {
88
- return moduleNames.map(name => {
89
- return resolveModuleName(name.text, containingFile, options, redirectedReference);
90
- });
134
+ return parsedSourceFiles.get(originalSourceFile);
91
135
  };
92
- options.host.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, options) => {
93
- return moduleNames.map(name => {
94
- return resolveModuleName(name, containingFile, options, redirectedReference).resolvedModule;
95
- });
96
- };
97
- const program = Reflect.apply(target, thisArg, [options]);
136
+ if (extensions.length) {
137
+ options.options.allowArbitraryExtensions = true;
138
+ const resolveModuleName = (0, resolveModuleName_1.createResolveModuleName)(ts, originalHost, language.plugins, fileName => language.scripts.get(fileName));
139
+ const resolveModuleNameLiterals = originalHost.resolveModuleNameLiterals;
140
+ const resolveModuleNames = originalHost.resolveModuleNames;
141
+ options.host.resolveModuleNameLiterals = (moduleLiterals, containingFile, redirectedReference, compilerOptions, ...rest) => {
142
+ if (resolveModuleNameLiterals && moduleLiterals.every(name => !extensions.some(ext => name.text.endsWith(ext)))) {
143
+ return resolveModuleNameLiterals(moduleLiterals, containingFile, redirectedReference, compilerOptions, ...rest);
144
+ }
145
+ return moduleLiterals.map(moduleLiteral => {
146
+ return resolveModuleName(moduleLiteral.text, containingFile, compilerOptions, moduleResolutionCache, redirectedReference);
147
+ });
148
+ };
149
+ options.host.resolveModuleNames = (moduleNames, containingFile, reusedNames, redirectedReference, compilerOptions, containingSourceFile) => {
150
+ if (resolveModuleNames && moduleNames.every(name => !extensions.some(ext => name.endsWith(ext)))) {
151
+ return resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, compilerOptions, containingSourceFile);
152
+ }
153
+ return moduleNames.map(moduleName => {
154
+ return resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionCache, redirectedReference).resolvedModule;
155
+ });
156
+ };
157
+ }
158
+ const program = Reflect.apply(target, thisArg, args);
98
159
  (0, decorateProgram_1.decorateProgram)(language, program);
99
160
  // TODO: #128
100
- program.__volar__ = { files: language };
161
+ program.__volar__ = { language };
101
162
  return program;
102
- function resolveModuleName(name, containingFile, options, redirectedReference) {
103
- const resolved = ts.resolveModuleName(name, containingFile, options, moduleResolutionHost, originalHost.getModuleResolutionCache?.(), redirectedReference);
104
- if (resolved.resolvedModule) {
105
- for (let i = 0; i < arbitraryExtensions.length; i++) {
106
- if (resolved.resolvedModule.resolvedFileName.endsWith(arbitraryExtensions[i])) {
107
- const sourceFileName = resolved.resolvedModule.resolvedFileName.slice(0, -arbitraryExtensions[i].length) + extensions[i];
108
- resolved.resolvedModule.resolvedFileName = sourceFileName;
109
- resolved.resolvedModule.extension = extensions[i];
110
- }
111
- }
112
- }
113
- return resolved;
114
- }
115
163
  },
116
164
  });
117
165
  }
@@ -1,7 +1,8 @@
1
1
  import { Language, CodeInformation, SourceMap, SourceScript } from '@volar/language-core';
2
2
  import type * as ts from 'typescript';
3
3
  export declare function transformCallHierarchyItem(language: Language, item: ts.CallHierarchyItem, filter: (data: CodeInformation) => boolean): ts.CallHierarchyItem;
4
- export declare function transformDiagnostic<T extends ts.Diagnostic>(language: Language, diagnostic: T): T | undefined;
4
+ export declare function transformDiagnostic<T extends ts.Diagnostic>(language: Language, diagnostic: T, isTsc: boolean): T | undefined;
5
+ export declare function fillSourceFileText(language: Language, sourceFile: ts.SourceFile): void;
5
6
  export declare function transformFileTextChanges(language: Language, changes: ts.FileTextChanges, filter: (data: CodeInformation) => boolean): ts.FileTextChanges | undefined;
6
7
  export declare function transformDocumentSpan<T extends ts.DocumentSpan>(language: Language, documentSpan: T, filter: (data: CodeInformation) => boolean, shouldFallback?: boolean): T | undefined;
7
8
  export declare function transformSpan(language: Language, fileName: string | undefined, textSpan: ts.TextSpan | undefined, filter: (data: CodeInformation) => boolean): {
@@ -12,3 +13,4 @@ export declare function transformTextChange(sourceScript: SourceScript, map: Sou
12
13
  export declare function transformTextSpan(sourceScript: SourceScript, map: SourceMap<CodeInformation>, textSpan: ts.TextSpan, filter: (data: CodeInformation) => boolean): ts.TextSpan | undefined;
13
14
  export declare function toSourceOffset(sourceScript: SourceScript, map: SourceMap, position: number, filter: (data: CodeInformation) => boolean): number | undefined;
14
15
  export declare function toGeneratedOffset(sourceScript: SourceScript, map: SourceMap, position: number, filter: (data: CodeInformation) => boolean): number | undefined;
16
+ export declare function forEachGeneratedOffset(sourceScript: SourceScript, map: SourceMap, position: number, filter: (data: CodeInformation) => boolean): Generator<number, void, unknown>;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toGeneratedOffset = exports.toSourceOffset = exports.transformTextSpan = exports.transformTextChange = exports.transformSpan = exports.transformDocumentSpan = exports.transformFileTextChanges = exports.transformDiagnostic = exports.transformCallHierarchyItem = void 0;
3
+ exports.forEachGeneratedOffset = exports.toGeneratedOffset = exports.toSourceOffset = exports.transformTextSpan = exports.transformTextChange = exports.transformSpan = exports.transformDocumentSpan = exports.transformFileTextChanges = exports.fillSourceFileText = exports.transformDiagnostic = exports.transformCallHierarchyItem = void 0;
4
4
  const language_core_1 = require("@volar/language-core");
5
5
  const utils_1 = require("./utils");
6
6
  const transformedDiagnostics = new WeakMap();
7
+ const transformedSourceFile = new WeakSet();
7
8
  function transformCallHierarchyItem(language, item, filter) {
8
9
  const span = transformSpan(language, item.file, item.span, filter);
9
10
  const selectionSpan = transformSpan(language, item.file, item.selectionSpan, filter);
@@ -14,13 +15,13 @@ function transformCallHierarchyItem(language, item, filter) {
14
15
  };
15
16
  }
16
17
  exports.transformCallHierarchyItem = transformCallHierarchyItem;
17
- function transformDiagnostic(language, diagnostic) {
18
+ function transformDiagnostic(language, diagnostic, isTsc) {
18
19
  if (!transformedDiagnostics.has(diagnostic)) {
19
20
  transformedDiagnostics.set(diagnostic, undefined);
20
21
  const { relatedInformation } = diagnostic;
21
22
  if (relatedInformation) {
22
23
  diagnostic.relatedInformation = relatedInformation
23
- .map(d => transformDiagnostic(language, d))
24
+ .map(d => transformDiagnostic(language, d, isTsc))
24
25
  .filter(utils_1.notEmpty);
25
26
  }
26
27
  if (diagnostic.file !== undefined
@@ -30,6 +31,9 @@ function transformDiagnostic(language, diagnostic) {
30
31
  if (serviceScript) {
31
32
  const sourceSpan = transformTextSpan(sourceScript, map, { start: diagnostic.start, length: diagnostic.length }, language_core_1.shouldReportDiagnostics);
32
33
  if (sourceSpan) {
34
+ if (isTsc) {
35
+ fillSourceFileText(language, diagnostic.file);
36
+ }
33
37
  transformedDiagnostics.set(diagnostic, {
34
38
  ...diagnostic,
35
39
  start: sourceSpan.start,
@@ -48,6 +52,19 @@ function transformDiagnostic(language, diagnostic) {
48
52
  return transformedDiagnostics.get(diagnostic);
49
53
  }
50
54
  exports.transformDiagnostic = transformDiagnostic;
55
+ // fix https://github.com/vuejs/language-tools/issues/4099 without `incremental`
56
+ function fillSourceFileText(language, sourceFile) {
57
+ if (transformedSourceFile.has(sourceFile)) {
58
+ return;
59
+ }
60
+ transformedSourceFile.add(sourceFile);
61
+ const [serviceScript, sourceScript] = (0, utils_1.getServiceScript)(language, sourceFile.fileName);
62
+ if (serviceScript) {
63
+ sourceFile.text = sourceScript.snapshot.getText(0, sourceScript.snapshot.getLength())
64
+ + sourceFile.text.substring(sourceScript.snapshot.getLength());
65
+ }
66
+ }
67
+ exports.fillSourceFileText = fillSourceFileText;
51
68
  function transformFileTextChanges(language, changes, filter) {
52
69
  const [_, source] = (0, utils_1.getServiceScript)(language, changes.fileName);
53
70
  if (source) {
@@ -72,13 +89,10 @@ exports.transformFileTextChanges = transformFileTextChanges;
72
89
  function transformDocumentSpan(language, documentSpan, filter, shouldFallback) {
73
90
  let textSpan = transformSpan(language, documentSpan.fileName, documentSpan.textSpan, filter);
74
91
  if (!textSpan && shouldFallback) {
75
- const [serviceScript] = (0, utils_1.getServiceScript)(language, documentSpan.fileName);
76
- if (serviceScript) {
77
- textSpan = {
78
- fileName: documentSpan.fileName,
79
- textSpan: { start: 0, length: 0 },
80
- };
81
- }
92
+ textSpan = {
93
+ fileName: documentSpan.fileName,
94
+ textSpan: { start: 0, length: 0 },
95
+ };
82
96
  }
83
97
  if (!textSpan) {
84
98
  return;
@@ -158,4 +172,12 @@ function toGeneratedOffset(sourceScript, map, position, filter) {
158
172
  }
159
173
  }
160
174
  exports.toGeneratedOffset = toGeneratedOffset;
175
+ function* forEachGeneratedOffset(sourceScript, map, position, filter) {
176
+ for (const [generateOffset, mapping] of map.getGeneratedOffsets(position)) {
177
+ if (filter(mapping.data)) {
178
+ yield generateOffset + sourceScript.snapshot.getLength();
179
+ }
180
+ }
181
+ }
182
+ exports.forEachGeneratedOffset = forEachGeneratedOffset;
161
183
  //# sourceMappingURL=transform.js.map
@@ -5,10 +5,14 @@ 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
7
  const resolveModuleName_1 = require("../resolveModuleName");
8
+ const common_1 = require("../common");
8
9
  const scriptVersions = new Map();
9
10
  const fsFileSnapshots = new Map();
10
11
  function createTypeScriptLanguage(ts, languagePlugins, projectHost) {
11
- const language = (0, language_core_1.createLanguage)(languagePlugins, projectHost.useCaseSensitiveFileNames, scriptId => {
12
+ const language = (0, language_core_1.createLanguage)([
13
+ ...languagePlugins,
14
+ common_1.fileLanguageIdProviderPlugin,
15
+ ], projectHost.useCaseSensitiveFileNames, scriptId => {
12
16
  const fileName = projectHost.scriptIdToFileName(scriptId);
13
17
  // opened files
14
18
  let snapshot = projectHost.getScriptSnapshot(fileName);
@@ -29,7 +33,7 @@ function createTypeScriptLanguage(ts, languagePlugins, projectHost) {
29
33
  snapshot = fsFileSnapshots.get(fileName)?.[1];
30
34
  }
31
35
  if (snapshot) {
32
- language.scripts.set(scriptId, projectHost.getLanguageId(scriptId), snapshot);
36
+ language.scripts.set(scriptId, snapshot);
33
37
  }
34
38
  else {
35
39
  language.scripts.delete(scriptId);
@@ -43,7 +47,7 @@ function createTypeScriptLanguage(ts, languagePlugins, projectHost) {
43
47
  }
44
48
  if (languagePlugins.some(language => language.typescript?.extraFileExtensions.length)) {
45
49
  // TODO: can this share between monorepo packages?
46
- const moduleCache = ts.createModuleResolutionCache(languageServiceHost.getCurrentDirectory(), languageServiceHost.useCaseSensitiveFileNames ? s => s : s => s.toLowerCase(), languageServiceHost.getCompilationSettings());
50
+ const moduleCache = ts.createModuleResolutionCache(languageServiceHost.getCurrentDirectory(), languageServiceHost.useCaseSensitiveFileNames?.() ? s => s : s => s.toLowerCase(), languageServiceHost.getCompilationSettings());
47
51
  const resolveModuleName = (0, resolveModuleName_1.createResolveModuleName)(ts, languageServiceHost, languagePlugins, fileName => language.scripts.get(projectHost.fileNameToScriptId(fileName)));
48
52
  let lastSysVersion = projectHost.getSystemVersion?.();
49
53
  languageServiceHost.resolveModuleNameLiterals = (moduleLiterals, containingFile, redirectedReference, options, sourceFile) => {
@@ -5,6 +5,7 @@ const decorateLanguageService_1 = require("../node/decorateLanguageService");
5
5
  const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
6
6
  const language_core_1 = require("@volar/language-core");
7
7
  const createLanguageServicePlugin_1 = require("./createLanguageServicePlugin");
8
+ const common_1 = require("../common");
8
9
  const externalFiles = new WeakMap();
9
10
  const decoratedLanguageServices = new WeakSet();
10
11
  const decoratedLanguageServiceHosts = new WeakSet();
@@ -52,17 +53,26 @@ function createAsyncLanguageServicePlugin(extensions, scriptKind, loadLanguagePl
52
53
  };
53
54
  }
54
55
  loadLanguagePlugins(ts, info).then(languagePlugins => {
55
- const language = (0, language_core_1.createLanguage)(languagePlugins, ts.sys.useCaseSensitiveFileNames, fileName => {
56
+ const syncedScriptVersions = new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames);
57
+ const language = (0, language_core_1.createLanguage)([
58
+ ...languagePlugins,
59
+ common_1.fileLanguageIdProviderPlugin,
60
+ ], ts.sys.useCaseSensitiveFileNames, fileName => {
61
+ const version = getScriptVersion(fileName);
62
+ if (syncedScriptVersions.get(fileName) === version) {
63
+ return;
64
+ }
65
+ syncedScriptVersions.set(fileName, version);
56
66
  const snapshot = getScriptSnapshot(fileName);
57
67
  if (snapshot) {
58
- language.scripts.set(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
68
+ language.scripts.set(fileName, snapshot);
59
69
  }
60
70
  else {
61
71
  language.scripts.delete(fileName);
62
72
  }
63
73
  });
64
74
  (0, decorateLanguageService_1.decorateLanguageService)(language, info.languageService);
65
- (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(language, info.languageServiceHost, ts);
75
+ (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
66
76
  info.project.markAsDirty();
67
77
  initialized = true;
68
78
  });
@@ -4,6 +4,7 @@ exports.arrayItemsEqual = exports.createLanguageServicePlugin = void 0;
4
4
  const decorateLanguageService_1 = require("../node/decorateLanguageService");
5
5
  const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
6
6
  const language_core_1 = require("@volar/language-core");
7
+ const common_1 = require("../common");
7
8
  const externalFiles = new WeakMap();
8
9
  const projectExternalFileExtensions = new WeakMap();
9
10
  const decoratedLanguageServices = new WeakSet();
@@ -23,17 +24,27 @@ function createLanguageServicePlugin(loadLanguagePlugins) {
23
24
  .flat();
24
25
  projectExternalFileExtensions.set(info.project, extensions);
25
26
  const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
26
- const language = (0, language_core_1.createLanguage)(languagePlugins, ts.sys.useCaseSensitiveFileNames, fileName => {
27
+ const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
28
+ const syncedScriptVersions = new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames);
29
+ const language = (0, language_core_1.createLanguage)([
30
+ ...languagePlugins,
31
+ common_1.fileLanguageIdProviderPlugin,
32
+ ], ts.sys.useCaseSensitiveFileNames, fileName => {
33
+ const version = getScriptVersion(fileName);
34
+ if (syncedScriptVersions.get(fileName) === version) {
35
+ return;
36
+ }
37
+ syncedScriptVersions.set(fileName, version);
27
38
  const snapshot = getScriptSnapshot(fileName);
28
39
  if (snapshot) {
29
- language.scripts.set(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
40
+ language.scripts.set(fileName, snapshot);
30
41
  }
31
42
  else {
32
43
  language.scripts.delete(fileName);
33
44
  }
34
45
  });
35
46
  (0, decorateLanguageService_1.decorateLanguageService)(language, info.languageService);
36
- (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(language, info.languageServiceHost, ts);
47
+ (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
37
48
  }
38
49
  return info.languageService;
39
50
  },
@@ -1,4 +1,5 @@
1
1
  import type * as ts from 'typescript';
2
2
  import type { LanguagePlugin } from '@volar/language-core';
3
3
  export declare let getLanguagePlugins: (ts: typeof import('typescript'), options: ts.CreateProgramOptions) => LanguagePlugin[];
4
- export declare function runTsc(tscPath: string, extensions: string[], _getLanguagePlugins: typeof getLanguagePlugins): void;
4
+ export declare let getLanguageId: (fileName: string) => string;
5
+ export declare function runTsc(tscPath: string, extensions: string[], _getLanguagePlugins: typeof getLanguagePlugins, _getLanguageId: typeof getLanguageId): void;
@@ -1,11 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runTsc = exports.getLanguagePlugins = void 0;
3
+ exports.runTsc = exports.getLanguageId = exports.getLanguagePlugins = void 0;
4
4
  const fs = require("fs");
5
5
  let getLanguagePlugins = () => [];
6
6
  exports.getLanguagePlugins = getLanguagePlugins;
7
- function runTsc(tscPath, extensions, _getLanguagePlugins) {
7
+ let getLanguageId = () => 'ts';
8
+ exports.getLanguageId = getLanguageId;
9
+ function runTsc(tscPath, extensions, _getLanguagePlugins, _getLanguageId) {
8
10
  exports.getLanguagePlugins = _getLanguagePlugins;
11
+ exports.getLanguageId = _getLanguageId;
9
12
  const proxyApiPath = require.resolve('../node/proxyCreateProgram');
10
13
  const readFileSync = fs.readFileSync;
11
14
  fs.readFileSync = (...args) => {
@@ -18,10 +21,12 @@ function runTsc(tscPath, extensions, _getLanguagePlugins) {
18
21
  tsc = replace(tsc, /allSupportedExtensions = .*(?=;)/, s => s + `.concat([[${extsText}]])`);
19
22
  // proxy createProgram
20
23
  tsc = replace(tsc, /function createProgram\(.+\) {/, s => `var createProgram = require(${JSON.stringify(proxyApiPath)}).proxyCreateProgram(`
21
- + `new Proxy({}, { get(_target, p, _receiver) { return eval(p); } } ), `
22
- + `_createProgram, `
23
- + `[${extsText}], `
24
- + `require(${JSON.stringify(__filename)}).getLanguagePlugins`
24
+ + [
25
+ `new Proxy({}, { get(_target, p, _receiver) { return eval(p); } } )`,
26
+ `_createProgram`,
27
+ `require(${JSON.stringify(__filename)}).getLanguagePlugins`,
28
+ `require(${JSON.stringify(__filename)}).getLanguageId`,
29
+ ].join(', ')
25
30
  + `);\n`
26
31
  + s.replace('createProgram', '_createProgram'));
27
32
  return tsc;
@@ -1,3 +1,3 @@
1
1
  import type { LanguagePlugin, SourceScript } from '@volar/language-core';
2
2
  import type * as ts from 'typescript';
3
- export declare function createResolveModuleName(ts: typeof import('typescript'), languageServiceHost: ts.LanguageServiceHost, languagePlugins: LanguagePlugin<any>[], getSourceScript: (fileName: string) => SourceScript | undefined): (moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions, cache?: ts.ModuleResolutionCache, redirectedReference?: ts.ResolvedProjectReference, resolutionMode?: ts.ResolutionMode) => ts.ResolvedModuleWithFailedLookupLocations;
3
+ export declare function createResolveModuleName(ts: typeof import('typescript'), host: ts.ModuleResolutionHost, languagePlugins: LanguagePlugin<any>[], getSourceScript: (fileName: string) => SourceScript | undefined): (moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions, cache?: ts.ModuleResolutionCache, redirectedReference?: ts.ResolvedProjectReference, resolutionMode?: ts.ResolutionMode) => ts.ResolvedModuleWithFailedLookupLocations;
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createResolveModuleName = void 0;
4
- function createResolveModuleName(ts, languageServiceHost, languagePlugins, getSourceScript) {
5
- const toPatchResults = new Map();
4
+ function createResolveModuleName(ts, host, languagePlugins, getSourceScript) {
5
+ const toSourceFileInfo = new Map();
6
6
  const moduleResolutionHost = {
7
- readFile: languageServiceHost.readFile.bind(languageServiceHost),
8
- directoryExists: languageServiceHost.directoryExists?.bind(languageServiceHost),
9
- realpath: languageServiceHost.realpath?.bind(languageServiceHost),
10
- getCurrentDirectory: languageServiceHost.getCurrentDirectory.bind(languageServiceHost),
11
- getDirectories: languageServiceHost.getDirectories?.bind(languageServiceHost),
12
- useCaseSensitiveFileNames: languageServiceHost.useCaseSensitiveFileNames?.bind(languageServiceHost),
7
+ readFile: host.readFile.bind(host),
8
+ directoryExists: host.directoryExists?.bind(host),
9
+ realpath: host.realpath?.bind(host),
10
+ getCurrentDirectory: host.getCurrentDirectory?.bind(host),
11
+ getDirectories: host.getDirectories?.bind(host),
12
+ useCaseSensitiveFileNames: typeof host.useCaseSensitiveFileNames === 'function'
13
+ ? host.useCaseSensitiveFileNames.bind(host)
14
+ : host.useCaseSensitiveFileNames,
13
15
  fileExists(fileName) {
14
16
  for (const { typescript } of languagePlugins) {
15
17
  if (!typescript) {
@@ -17,36 +19,51 @@ function createResolveModuleName(ts, languageServiceHost, languagePlugins, getSo
17
19
  }
18
20
  for (const { extension } of typescript.extraFileExtensions) {
19
21
  if (fileName.endsWith(`.d.${extension}.ts`)) {
20
- const patchFileName = fileName.slice(0, -`.d.${extension}.ts`.length) + `.${extension}`;
21
- if (fileExists(patchFileName)) {
22
- toPatchResults.set(fileName, patchFileName);
23
- return true;
22
+ const sourceFileName = fileName.slice(0, -`.d.${extension}.ts`.length) + `.${extension}`;
23
+ if (fileExists(sourceFileName)) {
24
+ const sourceScript = getSourceScript(sourceFileName);
25
+ if (sourceScript?.generated) {
26
+ const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
27
+ if (serviceScript) {
28
+ const dtsPath = sourceFileName + '.d.ts';
29
+ if ((serviceScript.extension === '.js' || serviceScript.extension === '.jsx') && fileExists(dtsPath)) {
30
+ toSourceFileInfo.set(fileName, {
31
+ sourceFileName: dtsPath,
32
+ extension: '.ts',
33
+ });
34
+ }
35
+ else {
36
+ toSourceFileInfo.set(fileName, {
37
+ sourceFileName,
38
+ extension: serviceScript.extension,
39
+ });
40
+ }
41
+ return true;
42
+ }
43
+ }
24
44
  }
25
45
  }
26
46
  }
27
47
  }
28
- return languageServiceHost.fileExists(fileName);
48
+ return host.fileExists(fileName);
29
49
  },
30
50
  };
31
51
  return (moduleName, containingFile, compilerOptions, cache, redirectedReference, resolutionMode) => {
32
52
  const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost, cache, redirectedReference, resolutionMode);
33
- if (result.resolvedModule && toPatchResults.has(result.resolvedModule.resolvedFileName)) {
34
- result.resolvedModule.resolvedFileName = toPatchResults.get(result.resolvedModule.resolvedFileName);
35
- const sourceScript = getSourceScript(result.resolvedModule.resolvedFileName);
36
- if (sourceScript?.generated) {
37
- const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
38
- if (serviceScript) {
39
- result.resolvedModule.extension = serviceScript.extension;
40
- }
53
+ if (result.resolvedModule) {
54
+ const sourceFileInfo = toSourceFileInfo.get(result.resolvedModule.resolvedFileName);
55
+ if (sourceFileInfo) {
56
+ result.resolvedModule.resolvedFileName = sourceFileInfo.sourceFileName;
57
+ result.resolvedModule.extension = sourceFileInfo.extension;
41
58
  }
42
59
  }
43
- toPatchResults.clear();
60
+ toSourceFileInfo.clear();
44
61
  return result;
45
62
  };
46
63
  // fix https://github.com/vuejs/language-tools/issues/3332
47
64
  function fileExists(fileName) {
48
- if (languageServiceHost.fileExists(fileName)) {
49
- const fileSize = ts.sys.getFileSize?.(fileName) ?? languageServiceHost.readFile(fileName)?.length ?? 0;
65
+ if (host.fileExists(fileName)) {
66
+ const fileSize = ts.sys.getFileSize?.(fileName) ?? host.readFile(fileName)?.length ?? 0;
50
67
  return fileSize < 4 * 1024 * 1024;
51
68
  }
52
69
  return false;