Skip to main content
Glama
SiroSuzume

MCP ts-morph Refactoring Tools

by SiroSuzume
internal-dependencies.test.ts9.12 kB
import { describe, it, expect } from "vitest"; import { type FunctionDeclaration, Project, SyntaxKind, type VariableStatement, } from "ts-morph"; import { findTopLevelDeclarationByName } from "./find-declaration"; import { getInternalDependencies } from "./internal-dependencies"; // --- Test Setup Helper --- const setupProject = () => { const project = new Project({ useInMemoryFileSystem: true, compilerOptions: { target: 99, module: 99 }, }); project.createDirectory("/src"); return project; }; describe("getInternalDependencies", () => { it("関数宣言が依存する内部関数と内部変数を特定できる", () => { const project = setupProject(); const filePath = "/src/internal-deps-advanced.ts"; const sourceFile = project.createSourceFile( filePath, ` const configValue = 10; const calculatedValue = configValue * 2; function helperFunc(n: number): number { return n + calculatedValue; } export function mainFunc(x: number): void { const result = helperFunc(x); console.log(result); } `, ); const mainFuncDecl = findTopLevelDeclarationByName( sourceFile, "mainFunc", SyntaxKind.FunctionDeclaration, ) as FunctionDeclaration; const helperFuncDecl = findTopLevelDeclarationByName( sourceFile, "helperFunc", SyntaxKind.FunctionDeclaration, ) as FunctionDeclaration; const calculatedValueStmt = findTopLevelDeclarationByName( sourceFile, "calculatedValue", SyntaxKind.VariableStatement, ) as VariableStatement; const configValueStmt = findTopLevelDeclarationByName( sourceFile, "configValue", SyntaxKind.VariableStatement, ) as VariableStatement; expect(mainFuncDecl).toBeDefined(); expect(helperFuncDecl).toBeDefined(); expect(calculatedValueStmt).toBeDefined(); expect(configValueStmt).toBeDefined(); const dependencies = getInternalDependencies(mainFuncDecl); expect(dependencies).toBeInstanceOf(Array); expect(dependencies).toHaveLength(3); // helperFunc, calculatedValue, configValue expect(dependencies).toEqual( expect.arrayContaining([ helperFuncDecl, calculatedValueStmt, configValueStmt, ]), ); }); it("関数宣言が依存する内部変数を特定できる (間接依存)", () => { const project = setupProject(); const filePath = "/src/internal-deps-advanced.ts"; const sourceFile = project.createSourceFile( filePath, ` const configValue = 10; // <- さらに依存 const calculatedValue = configValue * 2; // <- 依存先 function helperFunc(n: number): number { return n + calculatedValue; } // <- これを対象 `, ); const helperFuncDecl = findTopLevelDeclarationByName( sourceFile, "helperFunc", SyntaxKind.FunctionDeclaration, ) as FunctionDeclaration; const calculatedValueStmt = findTopLevelDeclarationByName( sourceFile, "calculatedValue", SyntaxKind.VariableStatement, ) as VariableStatement; const configValueStmt = findTopLevelDeclarationByName( sourceFile, "configValue", SyntaxKind.VariableStatement, ) as VariableStatement; expect(helperFuncDecl).toBeDefined(); expect(calculatedValueStmt).toBeDefined(); expect(configValueStmt).toBeDefined(); const dependencies = getInternalDependencies(helperFuncDecl); expect(dependencies).toBeInstanceOf(Array); expect(dependencies).toHaveLength(2); // calculatedValue, configValue expect(dependencies).toEqual( expect.arrayContaining([calculatedValueStmt, configValueStmt]), ); }); it("変数宣言が依存する内部変数を特定できる", () => { const project = setupProject(); const filePath = "/src/internal-deps-advanced.ts"; const sourceFile = project.createSourceFile( filePath, ` const configValue = 10; // <- さらに依存 const calculatedValue = configValue * 2; // <- 依存先 export const derivedConst = calculatedValue + 5; // <- これを対象 `, ); const derivedConstStmt = findTopLevelDeclarationByName( sourceFile, "derivedConst", SyntaxKind.VariableStatement, ) as VariableStatement; const calculatedValueStmt = findTopLevelDeclarationByName( sourceFile, "calculatedValue", SyntaxKind.VariableStatement, ) as VariableStatement; const configValueStmt = findTopLevelDeclarationByName( sourceFile, "configValue", SyntaxKind.VariableStatement, ) as VariableStatement; expect(derivedConstStmt).toBeDefined(); expect(calculatedValueStmt).toBeDefined(); expect(configValueStmt).toBeDefined(); const dependencies = getInternalDependencies(derivedConstStmt); expect(dependencies).toBeInstanceOf(Array); expect(dependencies).toHaveLength(2); // calculatedValue, configValue expect(dependencies).toEqual( expect.arrayContaining([calculatedValueStmt, configValueStmt]), ); }); it("変数宣言が依存する内部変数を特定できる (直接依存)", () => { const project = setupProject(); const filePath = "/src/internal-deps-advanced.ts"; const sourceFile = project.createSourceFile( filePath, ` const configValue = 10; // <- 依存先 const calculatedValue = configValue * 2; // <- これを対象 `, ); const calculatedValueStmt = findTopLevelDeclarationByName( sourceFile, "calculatedValue", SyntaxKind.VariableStatement, ) as VariableStatement; const configValueStmt = findTopLevelDeclarationByName( sourceFile, "configValue", SyntaxKind.VariableStatement, ) as VariableStatement; expect( calculatedValueStmt, "Test setup failed: calculatedValue not found", ).toBeDefined(); expect( configValueStmt, "Test setup failed: configValue not found", ).toBeDefined(); const dependencies = getInternalDependencies(calculatedValueStmt); expect(dependencies).toBeInstanceOf(Array); expect(dependencies).toHaveLength(1); expect(dependencies[0]).toBe(configValueStmt); }); it("依存関係がない場合は空配列を返す", () => { const project = setupProject(); const filePath = "/src/internal-deps-advanced.ts"; const sourceFile = project.createSourceFile( filePath, ` const configValue = 10; function unusedFunc() {} `, ); const configValueStmt = findTopLevelDeclarationByName( sourceFile, "configValue", SyntaxKind.VariableStatement, ) as VariableStatement; const unusedFuncDecl = findTopLevelDeclarationByName( sourceFile, "unusedFunc", SyntaxKind.FunctionDeclaration, ) as FunctionDeclaration; expect( configValueStmt, "Test setup failed: configValue not found", ).toBeDefined(); expect( unusedFuncDecl, "Test setup failed: unusedFunc not found", ).toBeDefined(); const configDeps = getInternalDependencies(configValueStmt); const unusedDeps = getInternalDependencies(unusedFuncDecl); expect(configDeps).toEqual([]); expect(unusedDeps).toEqual([]); }); it("関数宣言が依存する非エクスポートのアロー関数を特定できる", () => { const project = setupProject(); const filePath = "/src/arrow-func-dep.ts"; const sourceFile = project.createSourceFile( filePath, ` const arrowHelper = (n: number): number => n * n; export function mainFunc(x: number): number { return arrowHelper(x); } `, ); const mainFuncDecl = findTopLevelDeclarationByName( sourceFile, "mainFunc", SyntaxKind.FunctionDeclaration, ) as FunctionDeclaration; const arrowHelperStmt = findTopLevelDeclarationByName( sourceFile, "arrowHelper", SyntaxKind.VariableStatement, ) as VariableStatement; expect(mainFuncDecl).toBeDefined(); expect(arrowHelperStmt).toBeDefined(); const dependencies = getInternalDependencies(mainFuncDecl); expect(dependencies.length).toBe(1); expect(dependencies[0]).toBe(arrowHelperStmt); }); it("複数の間接的な内部依存関係を再帰的に特定できる", () => { const project = setupProject(); const filePath = "/src/recursive-deps.ts"; const sourceFile = project.createSourceFile( filePath, ` const d = 4; const c = () => d; const b = () => c(); export const a = () => b(); // a -> b -> c -> d const e = () => d; // d は a 以外からも参照されるが、ここでは a の依存のみ見る `, ); const aStmt = findTopLevelDeclarationByName( sourceFile, "a", SyntaxKind.VariableStatement, ) as VariableStatement; const bStmt = findTopLevelDeclarationByName( sourceFile, "b", SyntaxKind.VariableStatement, ) as VariableStatement; const cStmt = findTopLevelDeclarationByName( sourceFile, "c", SyntaxKind.VariableStatement, ) as VariableStatement; const dStmt = findTopLevelDeclarationByName( sourceFile, "d", SyntaxKind.VariableStatement, ) as VariableStatement; expect(aStmt).toBeDefined(); expect(bStmt).toBeDefined(); expect(cStmt).toBeDefined(); expect(dStmt).toBeDefined(); const dependencies = getInternalDependencies(aStmt); expect(dependencies).toBeInstanceOf(Array); expect(dependencies).toHaveLength(3); // b, c, d が含まれるはず expect(dependencies).toEqual(expect.arrayContaining([bStmt, cStmt, dStmt])); }); });

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