Skip to main content
Glama
SiroSuzume

MCP ts-morph Refactoring Tools

by SiroSuzume
update-module-specifiers.ts4.47 kB
import logger from "../../utils/logger"; import { calculateRelativePath } from "../_utils/calculate-relative-path"; import type { DeclarationToUpdate, RenameOperation } from "../types"; import * as path from "node:path"; import { performance } from "node:perf_hooks"; function findNewPath( oldFilePath: string, renameOperations: RenameOperation[], ): string | undefined { const operation = renameOperations.find((op) => op.oldPath === oldFilePath); return operation?.newPath; } export function updateModuleSpecifiers( allDeclarationsToUpdate: DeclarationToUpdate[], renameOperations: RenameOperation[], signal?: AbortSignal, ) { signal?.throwIfAborted(); const startTime = performance.now(); const PRESERVE_EXTENSIONS = [".js", ".jsx", ".json", ".mjs", ".cjs"]; logger.debug( { count: allDeclarationsToUpdate.length }, "Starting module specifier updates", ); let updatedCount = 0; let skippedCount = 0; for (const { declaration, resolvedPath, referencingFilePath, originalSpecifierText, wasPathAlias, } of allDeclarationsToUpdate) { signal?.throwIfAborted(); const moduleSpecifier = declaration.getModuleSpecifier(); if (!moduleSpecifier) { skippedCount++; logger.trace( { referencingFilePath, kind: declaration.getKindName() }, "Skipping declaration with no module specifier", ); continue; } const newReferencingFilePath = findNewPath(referencingFilePath, renameOperations) ?? referencingFilePath; const newResolvedPath = findNewPath(resolvedPath, renameOperations); if (!newResolvedPath) { skippedCount++; logger.warn( { resolvedPath, referencingFilePath: newReferencingFilePath }, "Could not determine new path for resolved path - Skipping update.", ); continue; } // TODO: wasPathAlias を使ってエイリアスパスを計算・維持するロジックを追加 let newSpecifier: string; // 元のインポートスタイルで index が省略されていたか判定 // (例: './utils', '../', '@/') // 注意: これは単純な判定であり、複雑なケースには対応できない可能性あり const wasIndexSimplified = /(\/|\/[^/.]+)$/.test(originalSpecifierText) || !path.extname(originalSpecifierText); logger.trace( { originalSpecifierText, wasIndexSimplified }, "Checked original specifier for index simplification", ); if (wasPathAlias) { // --- パスエイリアスを維持するロジック (仮) --- // 現時点では calculateRelativePath を使うが、将来的にはエイリアス計算に置き換える // tsconfig の paths と baseUrl が必要 logger.warn( { refFile: newReferencingFilePath, newResolved: newResolvedPath, originalSpecifier: originalSpecifierText, }, "Path alias preservation not fully implemented yet. Calculating relative path as fallback.", ); // ★★★ ここでエイリアスパスを計算するロジックが必要 ★★★ // 例: const newAliasPath = calculateAliasPath(project, newReferencingFilePath, newResolvedPath); // 仮に相対パスを計算。元のスタイルに合わせて simplifyIndex を設定。 newSpecifier = calculateRelativePath( newReferencingFilePath, newResolvedPath, { removeExtensions: !PRESERVE_EXTENSIONS.includes( path.extname(originalSpecifierText), ), simplifyIndex: wasIndexSimplified, // 元のスタイルに合わせる }, ); } else { // --- 相対パスなど、エイリアス以外の場合 --- newSpecifier = calculateRelativePath( newReferencingFilePath, newResolvedPath, { removeExtensions: !PRESERVE_EXTENSIONS.includes( path.extname(originalSpecifierText), ), simplifyIndex: wasIndexSimplified, // 元のスタイルに合わせる }, ); } try { // 計算した newSpecifier を設定 declaration.setModuleSpecifier(newSpecifier); updatedCount++; } catch (err) { skippedCount++; logger.error( { err, refFile: newReferencingFilePath, newResolved: newResolvedPath, originalSpecifier: originalSpecifierText, wasPathAlias, newSpecifier, // newRelativePath から変更 }, "Error setting module specifier, skipping update", ); } } const durationMs = (performance.now() - startTime).toFixed(2); logger.debug( { updated: updatedCount, skipped: skippedCount, durationMs }, "Finished module specifier updates", ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SiroSuzume/mcp-ts-morph'

If you have feedback or need assistance with the MCP directory API, please join our Discord server