diff --git a/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts b/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts index 0706046e4..a43a154b3 100644 --- a/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts +++ b/packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts @@ -1,16 +1,17 @@ import ts from 'typescript'; +import { TextDocumentContentChangeEvent } from 'vscode-languageserver'; import { Document, DocumentManager } from '../../lib/documents'; import { LSConfigManager } from '../../ls-config'; -import { debounceSameArg, pathToUrl } from '../../utils'; +import { debounceSameArg, normalizePath, pathToUrl } from '../../utils'; import { DocumentSnapshot, SvelteDocumentSnapshot } from './DocumentSnapshot'; import { getService, getServiceForTsconfig, - hasServiceForFile, + forAllServices, LanguageServiceContainer, LanguageServiceDocumentContext } from './service'; -import { SnapshotManager } from './SnapshotManager'; +import { GlobalSnapshotsManager, SnapshotManager } from './SnapshotManager'; export class LSAndTSDocResolver { /** @@ -59,10 +60,13 @@ export class LSAndTSDocResolver { return document; }; + private globalSnapshotsManager = new GlobalSnapshotsManager(); + private get lsDocumentContext(): LanguageServiceDocumentContext { return { createDocument: this.createDocument, - transformOnTemplateError: !this.tsconfigPath + transformOnTemplateError: !this.tsconfigPath, + globalSnapshotsManager: this.globalSnapshotsManager }; } @@ -82,6 +86,11 @@ export class LSAndTSDocResolver { return { tsDoc, lang, userPreferences }; } + /** + * Retrieves and updates the snapshot for the given document or path from + * the ts service it primarely belongs into. + * The update is mirrored in all other services, too. + */ async getSnapshot(document: Document): Promise; async getSnapshot(pathOrDoc: string | Document): Promise; async getSnapshot(pathOrDoc: string | Document) { @@ -90,21 +99,49 @@ export class LSAndTSDocResolver { return tsService.updateSnapshot(pathOrDoc); } + /** + * Updates snapshot path in all existing ts services and retrieves snapshot + */ async updateSnapshotPath(oldPath: string, newPath: string): Promise { await this.deleteSnapshot(oldPath); return this.getSnapshot(newPath); } + /** + * Deletes snapshot in all existing ts services + */ async deleteSnapshot(filePath: string) { - if (!hasServiceForFile(filePath, this.workspaceUris)) { - // Don't initialize a service for a file that should be deleted - return; - } - - (await this.getTSService(filePath)).deleteSnapshot(filePath); + await forAllServices((service) => service.deleteSnapshot(filePath)); this.docManager.releaseDocument(pathToUrl(filePath)); } + /** + * Updates project files in all existing ts services + */ + async updateProjectFiles() { + await forAllServices((service) => service.updateProjectFiles()); + } + + /** + * Updates file in all ts services where it exists + */ + async updateExistingTsOrJsFile( + path: string, + changes?: TextDocumentContentChangeEvent[] + ): Promise { + path = normalizePath(path); + // Only update once because all snapshots are shared between + // services. Since we don't have a current version of TS/JS + // files, the operation wouldn't be idempotent. + let didUpdate = false; + await forAllServices((service) => { + if (service.hasFile(path) && !didUpdate) { + didUpdate = true; + service.updateTsOrJsFile(path, changes); + } + }); + } + /** * @internal Public for tests only */ diff --git a/packages/language-server/src/plugins/typescript/SnapshotManager.ts b/packages/language-server/src/plugins/typescript/SnapshotManager.ts index c91b84b92..8b95b2a5f 100644 --- a/packages/language-server/src/plugins/typescript/SnapshotManager.ts +++ b/packages/language-server/src/plugins/typescript/SnapshotManager.ts @@ -2,6 +2,72 @@ import ts from 'typescript'; import { DocumentSnapshot, JSOrTSDocumentSnapshot } from './DocumentSnapshot'; import { Logger } from '../../logger'; import { TextDocumentContentChangeEvent } from 'vscode-languageserver'; +import { normalizePath } from '../../utils'; +import { EventEmitter } from 'events'; + +/** + * Every snapshot corresponds to a unique file on disk. + * A snapshot can be part of multiple projects, but for a given file path + * there can be only one snapshot. + */ +export class GlobalSnapshotsManager { + private emitter = new EventEmitter(); + private documents = new Map(); + + get(fileName: string) { + fileName = normalizePath(fileName); + return this.documents.get(fileName); + } + + set(fileName: string, document: DocumentSnapshot) { + fileName = normalizePath(fileName); + const prev = this.get(fileName); + if (prev) { + prev.destroyFragment(); + } + + this.documents.set(fileName, document); + this.emitter.emit('change', fileName, document); + } + + delete(fileName: string) { + fileName = normalizePath(fileName); + this.documents.delete(fileName); + this.emitter.emit('change', fileName, undefined); + } + + updateTsOrJsFile( + fileName: string, + changes?: TextDocumentContentChangeEvent[] + ): JSOrTSDocumentSnapshot | undefined { + fileName = normalizePath(fileName); + const previousSnapshot = this.get(fileName); + + if (changes) { + if (!(previousSnapshot instanceof JSOrTSDocumentSnapshot)) { + return; + } + previousSnapshot.update(changes); + return previousSnapshot; + } else { + const newSnapshot = DocumentSnapshot.fromNonSvelteFilePath(fileName); + + if (previousSnapshot) { + newSnapshot.version = previousSnapshot.version + 1; + } else { + // ensure it's greater than initial version + // so that ts server picks up the change + newSnapshot.version += 1; + } + this.set(fileName, newSnapshot); + return newSnapshot; + } + } + + onChange(listener: (fileName: string, newDocument: DocumentSnapshot | undefined) => void) { + this.emitter.on('change', listener); + } +} export interface TsFilesSpec { include?: readonly string[]; @@ -12,7 +78,7 @@ export interface TsFilesSpec { * Should only be used by `service.ts` */ export class SnapshotManager { - private documents: Map = new Map(); + private documents = new Map(); private lastLogged = new Date(new Date().getTime() - 60_001); private readonly watchExtensions = [ @@ -25,12 +91,26 @@ export class SnapshotManager { ]; constructor( + private globalSnapshotsManager: GlobalSnapshotsManager, private projectFiles: string[], private fileSpec: TsFilesSpec, private workspaceRoot: string - ) {} + ) { + this.globalSnapshotsManager.onChange((fileName, document) => { + // Only delete/update snapshots, don't add new ones, + // as they could be from another TS service and this + // snapshot manager can't reach this file. + // For these, instead wait on a `get` method invocation + // and set them "manually" in the set/update methods. + if (!document) { + this.documents.delete(fileName); + } else if (this.documents.has(fileName)) { + this.documents.set(fileName, document); + } + }); + } - updateProjectFiles() { + updateProjectFiles(): void { const { include, exclude } = this.fileSpec; // Since we default to not include anything, @@ -39,67 +119,58 @@ export class SnapshotManager { return; } - const projectFiles = ts.sys.readDirectory( - this.workspaceRoot, - this.watchExtensions, - exclude, - include - ); + const projectFiles = ts.sys + .readDirectory(this.workspaceRoot, this.watchExtensions, exclude, include) + .map(normalizePath); this.projectFiles = Array.from(new Set([...this.projectFiles, ...projectFiles])); } updateTsOrJsFile(fileName: string, changes?: TextDocumentContentChangeEvent[]): void { - const previousSnapshot = this.get(fileName); - - if (changes) { - if (!(previousSnapshot instanceof JSOrTSDocumentSnapshot)) { - return; - } - previousSnapshot.update(changes); - } else { - const newSnapshot = DocumentSnapshot.fromNonSvelteFilePath(fileName); - - if (previousSnapshot) { - newSnapshot.version = previousSnapshot.version + 1; - } else { - // ensure it's greater than initial version - // so that ts server picks up the change - newSnapshot.version += 1; - } - this.set(fileName, newSnapshot); + const snapshot = this.globalSnapshotsManager.updateTsOrJsFile(fileName, changes); + // This isn't duplicated logic to the listener, because this could + // be a new snapshot which the listener wouldn't add. + if (snapshot) { + this.documents.set(normalizePath(fileName), snapshot); } } - has(fileName: string) { + has(fileName: string): boolean { + fileName = normalizePath(fileName); return this.projectFiles.includes(fileName) || this.getFileNames().includes(fileName); } - set(fileName: string, snapshot: DocumentSnapshot) { - const prev = this.get(fileName); - if (prev) { - prev.destroyFragment(); - } - + set(fileName: string, snapshot: DocumentSnapshot): void { + this.globalSnapshotsManager.set(fileName, snapshot); + // This isn't duplicated logic to the listener, because this could + // be a new snapshot which the listener wouldn't add. + this.documents.set(normalizePath(fileName), snapshot); this.logStatistics(); - - return this.documents.set(fileName, snapshot); } - get(fileName: string) { - return this.documents.get(fileName); + get(fileName: string): DocumentSnapshot | undefined { + fileName = normalizePath(fileName); + let snapshot = this.documents.get(fileName); + if (!snapshot) { + snapshot = this.globalSnapshotsManager.get(fileName); + if (snapshot) { + this.documents.set(fileName, snapshot); + } + } + return snapshot; } - delete(fileName: string) { + delete(fileName: string): void { + fileName = normalizePath(fileName); this.projectFiles = this.projectFiles.filter((s) => s !== fileName); - return this.documents.delete(fileName); + this.globalSnapshotsManager.delete(fileName); } - getFileNames() { + getFileNames(): string[] { return Array.from(this.documents.keys()); } - getProjectFileNames() { + getProjectFileNames(): string[] { return [...this.projectFiles]; } diff --git a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts index 046628987..36edc19a5 100644 --- a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts +++ b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts @@ -61,7 +61,6 @@ import { SignatureHelpProviderImpl } from './features/SignatureHelpProvider'; import { UpdateImportsProviderImpl } from './features/UpdateImportsProvider'; import { isNoTextSpanInGeneratedCode, SnapshotFragmentMap } from './features/utils'; import { LSAndTSDocResolver } from './LSAndTSDocResolver'; -import { LanguageServiceContainer } from './service'; import { ignoredBuildDirectories } from './SnapshotManager'; import { convertToLocationRange, getScriptKindFromFileName, symbolKindFromString } from './utils'; @@ -371,7 +370,7 @@ export class TypeScriptPlugin } async onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesPara[]): Promise { - const doneUpdateProjectFiles = new Set(); + let doneUpdateProjectFiles = false; for (const { fileName, changeType } of onWatchFileChangesParas) { const pathParts = fileName.split(/\/|\\/); @@ -386,19 +385,13 @@ export class TypeScriptPlugin continue; } - const tsService = await this.lsAndTsDocResolver.getTSService(fileName); - if (changeType === FileChangeType.Created) { - if (!doneUpdateProjectFiles.has(tsService)) { - tsService.updateProjectFiles(); - doneUpdateProjectFiles.add(tsService); - } + if (changeType === FileChangeType.Created && !doneUpdateProjectFiles) { + doneUpdateProjectFiles = true; + await this.lsAndTsDocResolver.updateProjectFiles(); } else if (changeType === FileChangeType.Deleted) { - tsService.deleteSnapshot(fileName); - } else if (tsService.hasFile(fileName)) { - // Only allow existing files to be update - // Otherwise, new files would still get loaded - // into snapshot manager after update - tsService.updateTsOrJsFile(fileName); + await this.lsAndTsDocResolver.deleteSnapshot(fileName); + } else { + await this.lsAndTsDocResolver.updateExistingTsOrJsFile(fileName); } } } @@ -407,8 +400,7 @@ export class TypeScriptPlugin fileName: string, changes: TextDocumentContentChangeEvent[] ): Promise { - const snapshotManager = await this.getSnapshotManager(fileName); - snapshotManager.updateTsOrJsFile(fileName, changes); + await this.lsAndTsDocResolver.updateExistingTsOrJsFile(fileName, changes); } async getSelectionRange( diff --git a/packages/language-server/src/plugins/typescript/service.ts b/packages/language-server/src/plugins/typescript/service.ts index 122c97641..d6bb214a1 100644 --- a/packages/language-server/src/plugins/typescript/service.ts +++ b/packages/language-server/src/plugins/typescript/service.ts @@ -5,9 +5,14 @@ import { getPackageInfo } from '../../importPackage'; import { Document } from '../../lib/documents'; import { configLoader } from '../../lib/documents/configLoader'; import { Logger } from '../../logger'; +import { normalizePath } from '../../utils'; import { DocumentSnapshot } from './DocumentSnapshot'; import { createSvelteModuleLoader } from './module-loader'; -import { ignoredBuildDirectories, SnapshotManager } from './SnapshotManager'; +import { + GlobalSnapshotsManager, + ignoredBuildDirectories, + SnapshotManager +} from './SnapshotManager'; import { ensureRealSvelteFilePath, findTsConfigPath } from './utils'; export interface LanguageServiceContainer { @@ -39,6 +44,7 @@ const services = new Map>(); export interface LanguageServiceDocumentContext { transformOnTemplateError: boolean; createDocument: (fileName: string, content: string) => Document; + globalSnapshotsManager: GlobalSnapshotsManager; } export async function getService( @@ -50,9 +56,12 @@ export async function getService( return getServiceForTsconfig(tsconfigPath, docContext); } -export function hasServiceForFile(path: string, workspaceUris: string[]): boolean { - const tsconfigPath = findTsConfigPath(path, workspaceUris); - return services.has(tsconfigPath); +export async function forAllServices( + cb: (service: LanguageServiceContainer) => any +): Promise { + for (const service of services.values()) { + cb(await service); + } } /** @@ -85,7 +94,12 @@ async function createLanguageService( const { options: compilerOptions, fileNames: files, raw } = getParsedConfig(); // raw is the tsconfig merged with extending config // see: https://github.com/microsoft/TypeScript/blob/08e4f369fbb2a5f0c30dee973618d65e6f7f09f8/src/compiler/commandLineParser.ts#L2537 - const snapshotManager = new SnapshotManager(files, raw, workspacePath || process.cwd()); + const snapshotManager = new SnapshotManager( + docContext.globalSnapshotsManager, + files, + raw, + workspacePath || process.cwd() + ); // Load all configs within the tsconfig scope and the one above so that they are all loaded // by the time they need to be accessed synchronously by DocumentSnapshots to determine @@ -225,6 +239,7 @@ async function createLanguageService( } function fileBelongsToProject(filePath: string): boolean { + filePath = normalizePath(filePath); return hasFile(filePath) || getParsedConfig().fileNames.includes(filePath); } @@ -308,6 +323,7 @@ async function createLanguageService( return { ...parsedConfig, + fileNames: parsedConfig.fileNames.map(normalizePath), options: compilerOptions }; } diff --git a/packages/language-server/src/utils.ts b/packages/language-server/src/utils.ts index 055a0681d..8f704ebd2 100644 --- a/packages/language-server/src/utils.ts +++ b/packages/language-server/src/utils.ts @@ -17,6 +17,14 @@ export function pathToUrl(path: string) { return URI.file(path).toString(); } +/** + * Some paths (on windows) start with a upper case driver letter, some don't. + * This is normalized here. + */ +export function normalizePath(path: string): string { + return urlToPath(pathToUrl(path)) ?? ''; +} + /** * URIs coming from the client could be encoded in a different * way than expected / than the internal services create them. diff --git a/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts b/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts index 3fc0ba311..7f7b65e02 100644 --- a/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts +++ b/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts @@ -1,14 +1,14 @@ import * as assert from 'assert'; +import fs from 'fs'; import * as path from 'path'; import ts from 'typescript'; -import fs from 'fs'; import { CancellationTokenSource, FileChangeType, Position, Range } from 'vscode-languageserver'; import { Document, DocumentManager } from '../../../src/lib/documents'; import { LSConfigManager } from '../../../src/ls-config'; import { LSAndTSDocResolver, TypeScriptPlugin } from '../../../src/plugins'; import { INITIAL_VERSION } from '../../../src/plugins/typescript/DocumentSnapshot'; -import { pathToUrl, urlToPath } from '../../../src/utils'; import { ignoredBuildDirectories } from '../../../src/plugins/typescript/SnapshotManager'; +import { pathToUrl } from '../../../src/utils'; describe('TypescriptPlugin', () => { function getUri(filename: string) { @@ -372,20 +372,10 @@ describe('TypescriptPlugin', () => { }; }; - /** - * make it the same style of path delimiter as vscode's request - */ - const normalizeWatchFilePath = (path: string) => { - return urlToPath(pathToUrl(path)) ?? ''; - }; - const setupForOnWatchedFileUpdateOrDelete = async () => { const { plugin, snapshotManager, targetSvelteFile } = await setupForOnWatchedFileChanges(); - const projectJsFile = normalizeWatchFilePath( - path.join(path.dirname(targetSvelteFile), 'documentation.ts') - ); - + const projectJsFile = path.join(path.dirname(targetSvelteFile), 'documentation.ts'); await plugin.onWatchFileChanges([ { fileName: projectJsFile, @@ -441,7 +431,6 @@ describe('TypescriptPlugin', () => { const testForOnWatchedFileAdd = async (filePath: string, shouldExist: boolean) => { const { snapshotManager, plugin, targetSvelteFile } = await setupForOnWatchedFileChanges(); const addFile = path.join(path.dirname(targetSvelteFile), filePath); - const normalizedAddFilePath = normalizeWatchFilePath(addFile); const dir = path.dirname(addFile); if (!fs.existsSync(dir)) { @@ -451,25 +440,25 @@ describe('TypescriptPlugin', () => { assert.ok(fs.existsSync(addFile)); try { - assert.equal(snapshotManager.has(normalizedAddFilePath), false); + assert.equal(snapshotManager.has(addFile), false); await plugin.onWatchFileChanges([ { - fileName: normalizedAddFilePath, + fileName: addFile, changeType: FileChangeType.Created } ]); - assert.equal(snapshotManager.has(normalizedAddFilePath), shouldExist); + assert.equal(snapshotManager.has(addFile), shouldExist); await plugin.onWatchFileChanges([ { - fileName: normalizedAddFilePath, + fileName: addFile, changeType: FileChangeType.Changed } ]); - assert.equal(snapshotManager.has(normalizedAddFilePath), shouldExist); + assert.equal(snapshotManager.has(addFile), shouldExist); } finally { fs.unlinkSync(addFile); } diff --git a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts index da20233a2..ba8a23a1f 100644 --- a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts @@ -6,7 +6,7 @@ import { Document, DocumentManager } from '../../../../src/lib/documents'; import { LSConfigManager } from '../../../../src/ls-config'; import { DiagnosticsProviderImpl } from '../../../../src/plugins/typescript/features/DiagnosticsProvider'; import { LSAndTSDocResolver } from '../../../../src/plugins/typescript/LSAndTSDocResolver'; -import { pathToUrl, urlToPath } from '../../../../src/utils'; +import { normalizePath, pathToUrl } from '../../../../src/utils'; const testDir = path.join(__dirname, '..', 'testfiles', 'diagnostics'); @@ -945,7 +945,7 @@ describe('DiagnosticsProvider', () => { assert.deepStrictEqual(diagnostics1.length, 1); // back-and-forth-conversion normalizes slashes - const newFilePath = urlToPath(pathToUrl(path.join(testDir, 'doesntexistyet.js'))) || ''; + const newFilePath = normalizePath(path.join(testDir, 'doesntexistyet.js')) || ''; writeFileSync(newFilePath, 'export default function foo() {}'); assert.ok(existsSync(newFilePath)); await lsAndTsDocResolver.getSnapshot(newFilePath); @@ -953,7 +953,7 @@ describe('DiagnosticsProvider', () => { try { const diagnostics2 = await plugin.getDiagnostics(document); assert.deepStrictEqual(diagnostics2.length, 0); - lsAndTsDocResolver.deleteSnapshot(newFilePath); + await lsAndTsDocResolver.deleteSnapshot(newFilePath); } finally { unlinkSync(newFilePath); } @@ -962,6 +962,56 @@ describe('DiagnosticsProvider', () => { assert.deepStrictEqual(diagnostics3.length, 1); }).timeout(5000); + it('notices file changes in all services that reference that file', async () => { + const { plugin, document, docManager, lsAndTsDocResolver } = setup( + 'different-ts-service.svelte' + ); + const otherFilePath = path.join( + testDir, + 'different-ts-service', + 'different-ts-service.svelte' + ); + const otherDocument = docManager.openDocument({ + uri: pathToUrl(otherFilePath), + text: ts.sys.readFile(otherFilePath) || '' + }); + // needed because tests have nasty dependencies between them. The ts service + // is cached and knows the docs already + const sharedFilePath = path.join(testDir, 'shared-comp.svelte'); + docManager.openDocument({ + uri: pathToUrl(sharedFilePath), + text: ts.sys.readFile(sharedFilePath) || '' + }); + + const diagnostics1 = await plugin.getDiagnostics(document); + assert.deepStrictEqual(diagnostics1.length, 2); + const diagnostics2 = await plugin.getDiagnostics(otherDocument); + assert.deepStrictEqual(diagnostics2.length, 2); + + docManager.updateDocument( + { uri: pathToUrl(path.join(testDir, 'shared-comp.svelte')), version: 2 }, + [ + { + range: { start: { line: 1, character: 19 }, end: { line: 1, character: 19 } }, + text: 'o' + } + ] + ); + await lsAndTsDocResolver.updateExistingTsOrJsFile(path.join(testDir, 'shared-ts-file.ts'), [ + { + range: { start: { line: 0, character: 18 }, end: { line: 0, character: 18 } }, + text: 'r' + } + ]); + // Wait until the LsAndTsDocResolver notifies the services of the document update + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const diagnostics3 = await plugin.getDiagnostics(document); + assert.deepStrictEqual(diagnostics3.length, 0); + const diagnostics4 = await plugin.getDiagnostics(otherDocument); + assert.deepStrictEqual(diagnostics4.length, 0); + }).timeout(5000); + function assertPropsDiagnosticsStrict(diagnostics: any[], source: 'ts' | 'js') { assert.deepStrictEqual( diagnostics.map((d: any) => { diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service.svelte new file mode 100644 index 000000000..1920b2973 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service.svelte @@ -0,0 +1,5 @@ + diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service/different-ts-service.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service/different-ts-service.svelte new file mode 100644 index 000000000..28466189e --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service/different-ts-service.svelte @@ -0,0 +1,5 @@ + diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service/tsconfig.json b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service/tsconfig.json new file mode 100644 index 000000000..6d3385d79 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/different-ts-service/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + /** + This is actually not needed, but makes the tests faster + because TS does not look up other types. + */ + "types": ["svelte"] + } +} diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/shared-comp.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/shared-comp.svelte new file mode 100644 index 000000000..03842f701 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/shared-comp.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/shared-ts-file.ts b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/shared-ts-file.ts new file mode 100644 index 000000000..246dde747 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/shared-ts-file.ts @@ -0,0 +1 @@ +export function ba() {} diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json index ccbc2ad98..89dd24236 100644 --- a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/tsconfig.json @@ -6,5 +6,6 @@ because TS does not look up other types. */ "types": ["svelte"] - } + }, + "exclude": ["different-ts-service/**"] }