Skip to content

Commit 6180537

Browse files
authored
fix: copy global types into hidden folder in IDE contexts (#2561)
Since the global types now reference `svelte/...` imports, they need to be placed in the same context the users' Svelte package lives in. Else these imports would load the types of the Svelte version that comes bundled with the IDE. #2493 (side note: we had this problem before in #2109 when loading `svelte/elements`, but that solution is not applicable here)
1 parent 02b6b06 commit 6180537

File tree

9 files changed

+114
-45
lines changed

9 files changed

+114
-45
lines changed

packages/language-server/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
dist/
22
.vscode/
33
node_modules/
4-
!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/
4+
!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package

packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export class LSAndTSDocResolver {
113113
}
114114

115115
this.lsDocumentContext = {
116+
isSvelteCheck: !!this.options?.isSvelteCheck,
116117
ambientTypesSource: this.options?.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
117118
createDocument: this.createDocument,
118119
transformOnTemplateError: !this.options?.isSvelteCheck,

packages/language-server/src/plugins/typescript/service.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
isSvelteFilePath
3030
} from './utils';
3131
import { createProject, ProjectService } from './serviceCache';
32+
import { internalHelpers } from 'svelte2tsx';
3233

3334
export interface LanguageServiceContainer {
3435
readonly tsconfigPath: string;
@@ -132,6 +133,7 @@ export function __resetCache() {
132133
}
133134

134135
export interface LanguageServiceDocumentContext {
136+
isSvelteCheck: boolean;
135137
ambientTypesSource: string;
136138
transformOnTemplateError: boolean;
137139
createDocument: (fileName: string, content: string) => Document;
@@ -1211,25 +1213,13 @@ async function createLanguageService(
12111213
}
12121214

12131215
function getSvelteShimFiles() {
1214-
const isSvelte3 = sveltePackageInfo.version.major === 3;
1215-
const svelteHtmlDeclaration = isSvelte3
1216-
? undefined
1217-
: join(sveltePackageInfo.path, 'svelte-html.d.ts');
1218-
const svelteHtmlFallbackIfNotExist =
1219-
svelteHtmlDeclaration && tsSystem.fileExists(svelteHtmlDeclaration)
1220-
? svelteHtmlDeclaration
1221-
: './svelte-jsx-v4.d.ts';
1222-
1223-
const svelteTsxFiles = (
1224-
isSvelte3
1225-
? ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts']
1226-
: [
1227-
'./svelte-shims-v4.d.ts',
1228-
svelteHtmlFallbackIfNotExist,
1229-
'./svelte-native-jsx.d.ts'
1230-
]
1231-
).map((f) => tsSystem.resolvePath(resolve(svelteTsPath, f)));
1232-
1216+
const svelteTsxFiles = internalHelpers.get_global_types(
1217+
tsSystem,
1218+
sveltePackageInfo.version.major === 3,
1219+
sveltePackageInfo.path,
1220+
svelteTsPath,
1221+
docContext.isSvelteCheck ? undefined : tsconfigPath || workspacePath
1222+
);
12331223
const result = new FileSet(tsSystem.useCaseSensitiveFileNames);
12341224

12351225
svelteTsxFiles.forEach((f) => result.add(normalizePath(f)));

packages/language-server/test/plugins/typescript/service.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('service', () => {
2121

2222
const rootUris = [pathToUrl(testDir)];
2323
const lsDocumentContext: LanguageServiceDocumentContext = {
24+
isSvelteCheck: false,
2425
ambientTypesSource: 'svelte2tsx',
2526
createDocument(fileName, content) {
2627
return new Document(pathToUrl(fileName), content);

packages/svelte2tsx/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ export function emitDts(config: EmitDtsConfig): Promise<void>;
134134
* static top level `ts` namespace, it must be passed as a parameter.
135135
*/
136136
export const internalHelpers: {
137+
get_global_types: (
138+
tsSystem: ts.System,
139+
isSvelte3: boolean,
140+
sveltePath: string,
141+
typesPath: string,
142+
hiddenFolderPath?: string,
143+
) => string[],
137144
isKitFile: (
138145
fileName: string,
139146
options: InternalHelpers.KitFilesSettings

packages/svelte2tsx/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "svelte2tsx",
3-
"version": "0.7.0",
3+
"version": "0.7.23",
44
"description": "Convert Svelte components to TSX for type checking",
55
"author": "David Pershouse",
66
"license": "MIT",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { basename, dirname, join, resolve } from 'path';
2+
import type ts from 'typescript';
3+
4+
/**
5+
* Returns the path to the global svelte2tsx files that should be included in the project.
6+
* Creates a hidden folder in the user's node_modules if `hiddenFolderPath` is provided.
7+
*/
8+
export function get_global_types(
9+
tsSystem: ts.System,
10+
isSvelte3: boolean,
11+
sveltePath: string,
12+
typesPath: string,
13+
hiddenFolderPath?: string
14+
): string[] {
15+
const svelteHtmlPath = isSvelte3 ? undefined : join(sveltePath, 'svelte-html.d.ts');
16+
const svelteHtmlPathExists = svelteHtmlPath && tsSystem.fileExists(svelteHtmlPath);
17+
const svelteHtmlFile = svelteHtmlPathExists ? svelteHtmlPath : './svelte-jsx-v4.d.ts';
18+
19+
let svelteTsxFiles: string[];
20+
if (isSvelte3) {
21+
svelteTsxFiles = ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts'];
22+
} else {
23+
svelteTsxFiles = ['./svelte-shims-v4.d.ts', './svelte-native-jsx.d.ts'];
24+
if (!svelteHtmlPathExists) {
25+
svelteTsxFiles.push(svelteHtmlPath);
26+
}
27+
}
28+
svelteTsxFiles = svelteTsxFiles.map((f) => tsSystem.resolvePath(resolve(typesPath, f)));
29+
30+
if (hiddenFolderPath) {
31+
try {
32+
// IDE context - the `import('svelte')` statements inside the d.ts files will load the Svelte version of
33+
// the extension, which can cause all sorts of problems. Therefore put the files into a hidden folder in
34+
// the user's node_modules, preferably next to the Svelte package.
35+
let path = dirname(sveltePath);
36+
37+
if (!tsSystem.directoryExists(resolve(path, 'node_modules'))) {
38+
path = hiddenFolderPath;
39+
40+
while (path && !tsSystem.directoryExists(resolve(path, 'node_modules'))) {
41+
const parent = dirname(path);
42+
if (path === parent) {
43+
path = '';
44+
break;
45+
}
46+
path = parent;
47+
}
48+
}
49+
50+
if (path) {
51+
const hiddenPath = resolve(path, 'node_modules/.svelte2tsx-language-server-files');
52+
const newFiles = [];
53+
for (const f of svelteTsxFiles) {
54+
const hiddenFile = resolve(hiddenPath, basename(f));
55+
const existing = tsSystem.readFile(hiddenFile);
56+
const toWrite = tsSystem.readFile(f) || '';
57+
if (existing !== toWrite) {
58+
tsSystem.writeFile(hiddenFile, toWrite);
59+
}
60+
newFiles.push(hiddenFile);
61+
}
62+
svelteTsxFiles = newFiles;
63+
}
64+
} catch (e) {}
65+
}
66+
67+
if (svelteHtmlPathExists) {
68+
svelteTsxFiles.push(tsSystem.resolvePath(resolve(typesPath, svelteHtmlFile)));
69+
}
70+
71+
return svelteTsxFiles;
72+
}

packages/svelte2tsx/src/helpers/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { get_global_types } from './files';
12
import {
23
isHooksFile,
34
isKitFile,
@@ -23,5 +24,6 @@ export const internalHelpers = {
2324
upsertKitFile,
2425
toVirtualPos,
2526
toOriginalPos,
26-
findExports
27+
findExports,
28+
get_global_types
2729
};

packages/typescript-plugin/src/index.ts

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
importSvelteCompiler,
1313
isSvelteProject
1414
} from './utils';
15+
import { internalHelpers } from 'svelte2tsx';
1516

1617
function init(modules: { typescript: typeof ts }): ts.server.PluginModule {
1718
const configManager = new ConfigManager();
@@ -200,32 +201,27 @@ function init(modules: { typescript: typeof ts }): ts.server.PluginModule {
200201
}
201202

202203
const svelteTsPath = dirname(require.resolve('svelte2tsx'));
203-
const sveltePath = require.resolve(
204+
const svelteCompilerPath = require.resolve(
204205
'svelte/compiler',
205206
configFilePath ? { paths: [configFilePath] } : undefined
206207
);
207-
const VERSION = require(sveltePath).VERSION;
208-
const isSvelte3 = VERSION.split('.')[0] === '3';
209-
const svelteHtmlDeclaration = isSvelte3
210-
? undefined
211-
: join(dirname(sveltePath), 'svelte-html.d.ts');
212-
const svelteHtmlFallbackIfNotExist =
213-
svelteHtmlDeclaration && modules.typescript.sys.fileExists(svelteHtmlDeclaration)
214-
? svelteHtmlDeclaration
215-
: './svelte-jsx-v4.d.ts';
216-
const svelteTsxFiles = (
217-
isSvelte3
218-
? ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts']
219-
: [
220-
'./svelte-shims-v4.d.ts',
221-
svelteHtmlFallbackIfNotExist,
222-
'./svelte-native-jsx.d.ts'
223-
]
224-
).map((f) => modules.typescript.sys.resolvePath(resolve(svelteTsPath, f)));
225-
226-
resolvedSvelteTsxFiles = svelteTsxFiles;
227-
228-
return svelteTsxFiles;
208+
const sveltePath = dirname(
209+
require.resolve(
210+
'svelte/package.json',
211+
configFilePath ? { paths: [configFilePath] } : undefined
212+
)
213+
);
214+
const VERSION = require(svelteCompilerPath).VERSION;
215+
216+
resolvedSvelteTsxFiles = internalHelpers.get_global_types(
217+
modules.typescript.sys,
218+
VERSION.split('.')[0] === '3',
219+
sveltePath,
220+
svelteTsPath,
221+
configFilePath
222+
);
223+
224+
return resolvedSvelteTsxFiles;
229225
}
230226

231227
function isSvelteProjectWithCache(project: ts.server.Project) {

0 commit comments

Comments
 (0)