From afb9d6625d22c7ce72df48cc60f217bcc4a4dab8 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 5 Nov 2024 13:30:47 +0100 Subject: [PATCH 1/2] fix: copy global types into hidden folder in IDE contexts 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, when loading `svelte/elements`, and solved it through #9070, but that solution is not applicable here) --- .../plugins/typescript/LSAndTSDocResolver.ts | 1 + .../src/plugins/typescript/service.ts | 28 +++----- .../test/plugins/typescript/service.test.ts | 1 + packages/svelte2tsx/index.d.ts | 7 ++ packages/svelte2tsx/package.json | 2 +- packages/svelte2tsx/src/helpers/files.ts | 72 +++++++++++++++++++ packages/svelte2tsx/src/helpers/index.ts | 4 +- packages/typescript-plugin/src/index.ts | 42 +++++------ 8 files changed, 113 insertions(+), 44 deletions(-) create mode 100644 packages/svelte2tsx/src/helpers/files.ts diff --git a/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts b/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts index 3b0a76c06..b62680027 100644 --- a/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts +++ b/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts @@ -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, diff --git a/packages/language-server/src/plugins/typescript/service.ts b/packages/language-server/src/plugins/typescript/service.ts index caf8f0350..47321a531 100644 --- a/packages/language-server/src/plugins/typescript/service.ts +++ b/packages/language-server/src/plugins/typescript/service.ts @@ -29,6 +29,7 @@ import { isSvelteFilePath } from './utils'; import { createProject, ProjectService } from './serviceCache'; +import { internalHelpers } from 'svelte2tsx'; export interface LanguageServiceContainer { readonly tsconfigPath: string; @@ -132,6 +133,7 @@ export function __resetCache() { } export interface LanguageServiceDocumentContext { + isSvelteCheck: boolean; ambientTypesSource: string; transformOnTemplateError: boolean; createDocument: (fileName: string, content: string) => Document; @@ -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))); diff --git a/packages/language-server/test/plugins/typescript/service.test.ts b/packages/language-server/test/plugins/typescript/service.test.ts index e0aa9ffd4..360848fad 100644 --- a/packages/language-server/test/plugins/typescript/service.test.ts +++ b/packages/language-server/test/plugins/typescript/service.test.ts @@ -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); diff --git a/packages/svelte2tsx/index.d.ts b/packages/svelte2tsx/index.d.ts index 162d7df45..87c2fda52 100644 --- a/packages/svelte2tsx/index.d.ts +++ b/packages/svelte2tsx/index.d.ts @@ -134,6 +134,13 @@ export function emitDts(config: EmitDtsConfig): Promise; * 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 diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json index 7cf2706ef..6fd902108 100644 --- a/packages/svelte2tsx/package.json +++ b/packages/svelte2tsx/package.json @@ -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", diff --git a/packages/svelte2tsx/src/helpers/files.ts b/packages/svelte2tsx/src/helpers/files.ts new file mode 100644 index 000000000..a4bd1d2b4 --- /dev/null +++ b/packages/svelte2tsx/src/helpers/files.ts @@ -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; +} diff --git a/packages/svelte2tsx/src/helpers/index.ts b/packages/svelte2tsx/src/helpers/index.ts index ab252d21c..57ed548b7 100644 --- a/packages/svelte2tsx/src/helpers/index.ts +++ b/packages/svelte2tsx/src/helpers/index.ts @@ -1,3 +1,4 @@ +import { get_global_types } from './files'; import { isHooksFile, isKitFile, @@ -23,5 +24,6 @@ export const internalHelpers = { upsertKitFile, toVirtualPos, toOriginalPos, - findExports + findExports, + get_global_types }; diff --git a/packages/typescript-plugin/src/index.ts b/packages/typescript-plugin/src/index.ts index ccdf87a20..65b9c5f12 100644 --- a/packages/typescript-plugin/src/index.ts +++ b/packages/typescript-plugin/src/index.ts @@ -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(); @@ -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) { From 83bc832588a6e11c4f26d727c6f7e4cc528666d3 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 5 Nov 2024 13:32:34 +0100 Subject: [PATCH 2/2] update gitignore --- packages/language-server/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-server/.gitignore b/packages/language-server/.gitignore index 02c400bc6..25fe7c815 100644 --- a/packages/language-server/.gitignore +++ b/packages/language-server/.gitignore @@ -1,4 +1,4 @@ dist/ .vscode/ node_modules/ -!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/ \ No newline at end of file +!test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte/node_modules/package \ No newline at end of file