From 4b7cc78bb47b88ebd5d1da771aed81a04b80a4c2 Mon Sep 17 00:00:00 2001 From: Egoistian Date: Sun, 24 May 2026 22:11:16 +0900 Subject: [PATCH 1/3] fix tsconfig selection for monorepos --- src/shared.ts | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/shared.ts b/src/shared.ts index 0987256..4379a8b 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -6,23 +6,15 @@ import * as vscode from 'vscode' import type { FormatDiagnosticsHost } from 'typescript' import { log } from './logger' +import { findClosestTsconfig } from './tsconfigSelection' -export async function getTsconfigFile(path: string) { - let files = await vscode.workspace.findFiles('**/tsconfig.json', '**​/node_modules/**') - files = files.sort((a, b) => b.fsPath.length - a.fsPath.length) - .filter(p => !p.fsPath.includes('node_modules')) +export async function getTsconfigFile(filePath: string) { + const files = await vscode.workspace.findFiles('**/tsconfig.json', '**​/node_modules/**') + const tsconfigPath = findClosestTsconfig(filePath, files.map(file => file.fsPath)) - log({ path, files }) + log({ path: filePath, files, tsconfigPath }) - const pathSegments = path.split('/') - for (let i = pathSegments.length; i > 0; i--) { - const path = pathSegments.slice(0, i).join('/') - const tsConfigFile = files.find(file => file.fsPath.startsWith(path)) - if (tsConfigFile) - return tsConfigFile - } - - return files[0] + return files.find(file => file.fsPath === tsconfigPath) ?? files[0] } const formatDiagnosticsHost: FormatDiagnosticsHost = { From b992f06053a8e3e6b77cf8d71a1829c469ee99fd Mon Sep 17 00:00:00 2001 From: Egoistian Date: Sun, 24 May 2026 22:11:29 +0900 Subject: [PATCH 2/3] add tsconfig selection helper --- src/tsconfigSelection.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/tsconfigSelection.ts diff --git a/src/tsconfigSelection.ts b/src/tsconfigSelection.ts new file mode 100644 index 0000000..c09b993 --- /dev/null +++ b/src/tsconfigSelection.ts @@ -0,0 +1,21 @@ +import path from 'node:path' + +export function findClosestTsconfig(filePath: string, tsconfigPaths: string[]): string | undefined { + const normalizedFilePath = normalizePath(filePath) + const candidates = tsconfigPaths + .filter(tsconfigPath => !isNodeModulesPath(tsconfigPath)) + .sort((a, b) => b.length - a.length) + + return candidates.find((tsconfigPath) => { + const configDirectory = normalizePath(path.dirname(tsconfigPath)) + return normalizedFilePath === configDirectory || normalizedFilePath.startsWith(`${configDirectory}/`) + }) ?? candidates[0] +} + +function normalizePath(filePath: string) { + return filePath.replaceAll('\\', '/').replace(/\/+$/, '') +} + +function isNodeModulesPath(filePath: string) { + return normalizePath(filePath).split('/').includes('node_modules') +} From 72aeeaf113d4735ef1eee7c2d6c41d96648b99fe Mon Sep 17 00:00:00 2001 From: Egoistian Date: Sun, 24 May 2026 22:11:46 +0900 Subject: [PATCH 3/3] test tsconfig selection for monorepos --- test/tsconfigSelection.test.ts | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/tsconfigSelection.test.ts diff --git a/test/tsconfigSelection.test.ts b/test/tsconfigSelection.test.ts new file mode 100644 index 0000000..7b7d04b --- /dev/null +++ b/test/tsconfigSelection.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest' + +import { findClosestTsconfig } from '../src/tsconfigSelection' + +describe('findClosestTsconfig', () => { + it('selects the nearest tsconfig that contains the target file', () => { + expect(findClosestTsconfig( + '/repo/packages/app/src/index.ts', + [ + '/repo/tsconfig.json', + '/repo/packages/app/tsconfig.json', + ], + )).toBe('/repo/packages/app/tsconfig.json') + }) + + it('does not select a sibling package tsconfig for a package without one', () => { + expect(findClosestTsconfig( + '/repo/packages/app-b/src/index.ts', + [ + '/repo/tsconfig.json', + '/repo/packages/app-a/tsconfig.json', + ], + )).toBe('/repo/tsconfig.json') + }) + + it('ignores node_modules tsconfig files', () => { + expect(findClosestTsconfig( + '/repo/packages/app/src/index.ts', + [ + '/repo/packages/app/node_modules/some-package/tsconfig.json', + '/repo/tsconfig.json', + ], + )).toBe('/repo/tsconfig.json') + }) + + it('handles Windows-style paths', () => { + expect(findClosestTsconfig( + 'C:\\repo\\packages\\app\\src\\index.ts', + [ + 'C:\\repo\\tsconfig.json', + 'C:\\repo\\packages\\app\\tsconfig.json', + ], + )).toBe('C:\\repo\\packages\\app\\tsconfig.json') + }) +})