diff --git a/package.json b/package.json index 739abb88..a6d90c2f 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "run:sample:highlight": "ts-node -O '{\"module\": \"commonjs\"}' ./src/index.ts -d './dummy_project' --include includeFiles config --exclude excludeFiles utils --abstraction abstractions --LR --highlight config.ts b.ts", "run:sample:link": "ts-node -O '{\"module\": \"commonjs\"}' ./src/index.ts -d './dummy_project' --mermaid-link", "run:sample:README": "ts-node -O '{\"module\": \"commonjs\"}' ./src/index.ts -d /Users/horiyousuke/Documents/dev/numberplace --include src/components/atoms/ConfigMenu --exclude test stories node_modules", + "run:instability": "ts-node -O '{\"module\": \"commonjs\"}' ./src/index.ts --measure-instability", "build": "tsc", "build:re": "rm -r ./dist && tsc", "commit": "git-cz", diff --git a/src/index.ts b/src/index.ts index 7d55726a..0b060aff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,6 +45,10 @@ program ) .option('--LR', 'Specify Flowchart orientation Left-to-Right') .option('--TB', 'Specify Flowchart orientation Top-to-Bottom') + .option( + '--measure-instability', + 'Enable the beta feature to measure the instability of the modules', + ) .option( '--config-file', 'Specify the relative path to the config file (from cwd or specified by -d, --dir). Default is .tsgrc.json.', @@ -61,6 +65,27 @@ export async function main( const { graph: fullGraph, meta } = createGraph(dir); + let couplingData: { + afferentCoupling: number; + efferentCoupling: number; + path: string; + name: string; + isDirectory?: boolean | undefined; + }[] = []; + if (commandOptions.measureInstability) { + console.time('coupling'); + couplingData = fullGraph.nodes.map(node => { + const afferentCoupling = fullGraph.relations.filter( + r => r.kind === 'depends_on' && r.to.path === node.path, + ).length; + const efferentCoupling = fullGraph.relations.filter( + r => r.kind === 'depends_on' && r.from.path === node.path, + ).length; + return { ...node, afferentCoupling, efferentCoupling }; + }); + console.timeEnd('coupling'); + } + const graph = pipe( fullGraph, graph => @@ -73,11 +98,16 @@ export async function main( graph => highlight(commandOptions.highlight, graph), ); - await writeMarkdownFile(commandOptions.md ?? 'typescript-graph', graph, { - ...commandOptions, - rootDir: meta.rootDir, - executedScript: commandOptions.executedScript, - }); + await writeMarkdownFile( + commandOptions.md ?? 'typescript-graph', + graph, + { + ...commandOptions, + rootDir: meta.rootDir, + executedScript: commandOptions.executedScript, + }, + couplingData, + ); } const dir = path.resolve(opt.dir ?? './'); diff --git a/src/models.ts b/src/models.ts index c9e76794..2a3745c0 100644 --- a/src/models.ts +++ b/src/models.ts @@ -9,6 +9,7 @@ export type OptionValues = { LR: boolean; TB: boolean; configFile: string; + measureInstability: boolean; }; type FileName = string; diff --git a/src/writeMarkdownFile.ts b/src/writeMarkdownFile.ts index 3ec94f43..db38f7ed 100644 --- a/src/writeMarkdownFile.ts +++ b/src/writeMarkdownFile.ts @@ -11,6 +11,13 @@ export async function writeMarkdownFile( markdownTitle: string, graph: Graph, options: Options, + couplingData: { + afferentCoupling: number; + efferentCoupling: number; + path: string; + name: string; + isDirectory?: boolean | undefined; + }[], ) { return new Promise((resolve, reject) => { const filename = markdownTitle.endsWith('.md') @@ -31,6 +38,37 @@ export async function writeMarkdownFile( ws.write('```mermaid\n'); mermaidify(str => ws.write(str), graph, options); ws.write('```\n'); + + if (couplingData.length !== 0) { + ws.write('## Instability\n'); + ws.write('\n'); + ws.write( + 'module name | Afferent
coupling | Efferent
coupling | Instability\n', + ); + ws.write('--|--|--|--\n'); + + couplingData + .filter(node => !node.isDirectory) + // node_modules 配下のモジュールを除外する + .filter(node => !node.path.includes('node_modules')) + // json ファイルを除外する + .filter(node => !node.path.endsWith('.json')) + .map(node => { + const totalCoupling = node.afferentCoupling + node.efferentCoupling; + const instability = + totalCoupling === 0 ? 0 : node.efferentCoupling / totalCoupling; + return { ...node, instability }; + }) + .toSorted((a, b) => b.instability - a.instability) + .forEach(node => { + const totalCoupling = node.afferentCoupling + node.efferentCoupling; + const instability = + totalCoupling === 0 ? 0 : node.efferentCoupling / totalCoupling; + ws.write( + `${node.name} | ${node.afferentCoupling} | ${node.efferentCoupling} | ${instability.toFixed(2)}\n`, + ); + }); + } ws.end(); console.log(filename);