Skip to content

Commit

Permalink
fix: copy global types into hidden folder in IDE contexts (#2561)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
dummdidumm authored Nov 6, 2024
1 parent 02b6b06 commit 6180537
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 45 deletions.
2 changes: 1 addition & 1 deletion packages/language-server/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dist/
.vscode/
node_modules/
!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/
!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export class LSAndTSDocResolver {
}

this.lsDocumentContext = {
isSvelteCheck: !!this.options?.isSvelteCheck,
ambientTypesSource: this.options?.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
createDocument: this.createDocument,
transformOnTemplateError: !this.options?.isSvelteCheck,
Expand Down
28 changes: 9 additions & 19 deletions packages/language-server/src/plugins/typescript/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
isSvelteFilePath
} from './utils';
import { createProject, ProjectService } from './serviceCache';
import { internalHelpers } from 'svelte2tsx';

export interface LanguageServiceContainer {
readonly tsconfigPath: string;
Expand Down Expand Up @@ -132,6 +133,7 @@ export function __resetCache() {
}

export interface LanguageServiceDocumentContext {
isSvelteCheck: boolean;
ambientTypesSource: string;
transformOnTemplateError: boolean;
createDocument: (fileName: string, content: string) => Document;
Expand Down Expand Up @@ -1211,25 +1213,13 @@ async function createLanguageService(
}

function getSvelteShimFiles() {
const isSvelte3 = sveltePackageInfo.version.major === 3;
const svelteHtmlDeclaration = isSvelte3
? undefined
: join(sveltePackageInfo.path, 'svelte-html.d.ts');
const svelteHtmlFallbackIfNotExist =
svelteHtmlDeclaration && tsSystem.fileExists(svelteHtmlDeclaration)
? svelteHtmlDeclaration
: './svelte-jsx-v4.d.ts';

const svelteTsxFiles = (
isSvelte3
? ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts']
: [
'./svelte-shims-v4.d.ts',
svelteHtmlFallbackIfNotExist,
'./svelte-native-jsx.d.ts'
]
).map((f) => tsSystem.resolvePath(resolve(svelteTsPath, f)));

const svelteTsxFiles = internalHelpers.get_global_types(
tsSystem,
sveltePackageInfo.version.major === 3,
sveltePackageInfo.path,
svelteTsPath,
docContext.isSvelteCheck ? undefined : tsconfigPath || workspacePath
);
const result = new FileSet(tsSystem.useCaseSensitiveFileNames);

svelteTsxFiles.forEach((f) => result.add(normalizePath(f)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('service', () => {

const rootUris = [pathToUrl(testDir)];
const lsDocumentContext: LanguageServiceDocumentContext = {
isSvelteCheck: false,
ambientTypesSource: 'svelte2tsx',
createDocument(fileName, content) {
return new Document(pathToUrl(fileName), content);
Expand Down
7 changes: 7 additions & 0 deletions packages/svelte2tsx/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ export function emitDts(config: EmitDtsConfig): Promise<void>;
* static top level `ts` namespace, it must be passed as a parameter.
*/
export const internalHelpers: {
get_global_types: (
tsSystem: ts.System,
isSvelte3: boolean,
sveltePath: string,
typesPath: string,
hiddenFolderPath?: string,
) => string[],
isKitFile: (
fileName: string,
options: InternalHelpers.KitFilesSettings
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte2tsx/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "svelte2tsx",
"version": "0.7.0",
"version": "0.7.23",
"description": "Convert Svelte components to TSX for type checking",
"author": "David Pershouse",
"license": "MIT",
Expand Down
72 changes: 72 additions & 0 deletions packages/svelte2tsx/src/helpers/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { basename, dirname, join, resolve } from 'path';
import type ts from 'typescript';

/**
* Returns the path to the global svelte2tsx files that should be included in the project.
* Creates a hidden folder in the user's node_modules if `hiddenFolderPath` is provided.
*/
export function get_global_types(
tsSystem: ts.System,
isSvelte3: boolean,
sveltePath: string,
typesPath: string,
hiddenFolderPath?: string
): string[] {
const svelteHtmlPath = isSvelte3 ? undefined : join(sveltePath, 'svelte-html.d.ts');
const svelteHtmlPathExists = svelteHtmlPath && tsSystem.fileExists(svelteHtmlPath);
const svelteHtmlFile = svelteHtmlPathExists ? svelteHtmlPath : './svelte-jsx-v4.d.ts';

let svelteTsxFiles: string[];
if (isSvelte3) {
svelteTsxFiles = ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts'];
} else {
svelteTsxFiles = ['./svelte-shims-v4.d.ts', './svelte-native-jsx.d.ts'];
if (!svelteHtmlPathExists) {
svelteTsxFiles.push(svelteHtmlPath);
}
}
svelteTsxFiles = svelteTsxFiles.map((f) => tsSystem.resolvePath(resolve(typesPath, f)));

if (hiddenFolderPath) {
try {
// IDE context - the `import('svelte')` statements inside the d.ts files will load the Svelte version of
// the extension, which can cause all sorts of problems. Therefore put the files into a hidden folder in
// the user's node_modules, preferably next to the Svelte package.
let path = dirname(sveltePath);

if (!tsSystem.directoryExists(resolve(path, 'node_modules'))) {
path = hiddenFolderPath;

while (path && !tsSystem.directoryExists(resolve(path, 'node_modules'))) {
const parent = dirname(path);
if (path === parent) {
path = '';
break;
}
path = parent;
}
}

if (path) {
const hiddenPath = resolve(path, 'node_modules/.svelte2tsx-language-server-files');
const newFiles = [];
for (const f of svelteTsxFiles) {
const hiddenFile = resolve(hiddenPath, basename(f));
const existing = tsSystem.readFile(hiddenFile);
const toWrite = tsSystem.readFile(f) || '';
if (existing !== toWrite) {
tsSystem.writeFile(hiddenFile, toWrite);
}
newFiles.push(hiddenFile);
}
svelteTsxFiles = newFiles;
}
} catch (e) {}
}

if (svelteHtmlPathExists) {
svelteTsxFiles.push(tsSystem.resolvePath(resolve(typesPath, svelteHtmlFile)));
}

return svelteTsxFiles;
}
4 changes: 3 additions & 1 deletion packages/svelte2tsx/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { get_global_types } from './files';
import {
isHooksFile,
isKitFile,
Expand All @@ -23,5 +24,6 @@ export const internalHelpers = {
upsertKitFile,
toVirtualPos,
toOriginalPos,
findExports
findExports,
get_global_types
};
42 changes: 19 additions & 23 deletions packages/typescript-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
importSvelteCompiler,
isSvelteProject
} from './utils';
import { internalHelpers } from 'svelte2tsx';

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

const svelteTsPath = dirname(require.resolve('svelte2tsx'));
const sveltePath = require.resolve(
const svelteCompilerPath = require.resolve(
'svelte/compiler',
configFilePath ? { paths: [configFilePath] } : undefined
);
const VERSION = require(sveltePath).VERSION;
const isSvelte3 = VERSION.split('.')[0] === '3';
const svelteHtmlDeclaration = isSvelte3
? undefined
: join(dirname(sveltePath), 'svelte-html.d.ts');
const svelteHtmlFallbackIfNotExist =
svelteHtmlDeclaration && modules.typescript.sys.fileExists(svelteHtmlDeclaration)
? svelteHtmlDeclaration
: './svelte-jsx-v4.d.ts';
const svelteTsxFiles = (
isSvelte3
? ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts']
: [
'./svelte-shims-v4.d.ts',
svelteHtmlFallbackIfNotExist,
'./svelte-native-jsx.d.ts'
]
).map((f) => modules.typescript.sys.resolvePath(resolve(svelteTsPath, f)));

resolvedSvelteTsxFiles = svelteTsxFiles;

return svelteTsxFiles;
const sveltePath = dirname(
require.resolve(
'svelte/package.json',
configFilePath ? { paths: [configFilePath] } : undefined
)
);
const VERSION = require(svelteCompilerPath).VERSION;

resolvedSvelteTsxFiles = internalHelpers.get_global_types(
modules.typescript.sys,
VERSION.split('.')[0] === '3',
sveltePath,
svelteTsPath,
configFilePath
);

return resolvedSvelteTsxFiles;
}

function isSvelteProjectWithCache(project: ts.server.Project) {
Expand Down

0 comments on commit 6180537

Please sign in to comment.