dittory 0.0.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/dittory.svg)](https://www.npmjs.com/package/dittory)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A static analysis CLI that detects **parameters that always receive the same value** in React components and functions.
6
+ A static analysis CLI for TypeScript projects that detects **parameters that always receive the same value**.
7
7
 
8
8
  ## Why?
9
9
 
@@ -17,36 +17,49 @@ dittory helps you identify these opportunities automatically.
17
17
 
18
18
  ## Installation
19
19
 
20
- ```bash
21
- npm install -g dittory
20
+ ```sh
21
+ # npm
22
+ $ npm install -D dittory
23
+
24
+ # pnpm
25
+ $ pnpm add -D dittory
26
+
27
+ # yarn
28
+ $ yarn add -D dittory
29
+
30
+ # bun
31
+ $ bun add -D dittory
22
32
  ```
23
33
 
24
- Or use directly with npx:
34
+ Or run directly without installing:
25
35
 
26
- ```bash
27
- npx dittory
36
+ ```sh
37
+ $ npx dittory
38
+ $ pnpm dlx dittory
39
+ $ yarn dlx dittory
40
+ $ bunx dittory
28
41
  ```
29
42
 
30
43
  ## Usage
31
44
 
32
- ```bash
45
+ ```sh
33
46
  # Analyze ./src directory (default)
34
- dittory
47
+ $ dittory
35
48
 
36
49
  # Analyze a specific directory
37
- dittory ./path/to/src
50
+ $ dittory ./path/to/src
38
51
 
39
52
  # Set minimum usage count (default: 2)
40
- dittory --min=3
53
+ $ dittory --min=3
41
54
 
42
55
  # Analyze specific targets
43
- dittory --target=components # React components only
44
- dittory --target=functions # Functions and class methods only
45
- dittory --target=all # Both (default)
56
+ $ dittory --target=components # React components only
57
+ $ dittory --target=functions # Functions and class methods only
58
+ $ dittory --target=all # Both (default)
46
59
 
47
60
  # Output mode
48
- dittory --output=simple # Show only constant parameters (default)
49
- dittory --output=verbose # Also show all analyzed functions
61
+ $ dittory --output=simple # Show only constant parameters (default)
62
+ $ dittory --output=verbose # Also show all analyzed functions
50
63
  ```
51
64
 
52
65
  ## Example Output
@@ -274,7 +287,7 @@ const data = await fetchData(id);
274
287
  ## Requirements
275
288
 
276
289
  - Node.js >= 18
277
- - Project must have a `tsconfig.json`
290
+ - TypeScript project with a `tsconfig.json`
278
291
 
279
292
  ## License
280
293
 
package/dist/cli.mjs CHANGED
@@ -64,10 +64,9 @@ async function loadJsConfig(configPath) {
64
64
  }
65
65
  const VALID_TARGETS$1 = [
66
66
  "all",
67
- "components",
67
+ "react-components",
68
68
  "functions"
69
69
  ];
70
- const VALID_OUTPUTS$1 = ["simple", "verbose"];
71
70
  /**
72
71
  * コンフィグの値を検証する
73
72
  */
@@ -81,9 +80,9 @@ function validateConfig(config) {
81
80
  if (!VALID_TARGETS$1.includes(config.target)) throw new Error(`Invalid config: target must be one of ${VALID_TARGETS$1.join(", ")}, got ${config.target}`);
82
81
  result.target = config.target;
83
82
  }
84
- if ("output" in config) {
85
- if (!VALID_OUTPUTS$1.includes(config.output)) throw new Error(`Invalid config: output must be one of ${VALID_OUTPUTS$1.join(", ")}, got ${config.output}`);
86
- result.output = config.output;
83
+ if ("debug" in config) {
84
+ if (typeof config.debug !== "boolean") throw new Error(`Invalid config: debug must be a boolean, got ${typeof config.debug}`);
85
+ result.debug = config.debug;
87
86
  }
88
87
  if ("tsconfig" in config) {
89
88
  if (typeof config.tsconfig !== "string" || config.tsconfig === "") throw new Error(`Invalid config: tsconfig must be a non-empty string, got ${config.tsconfig}`);
@@ -111,7 +110,7 @@ const DEFAULT_OPTIONS = {
111
110
  targetDir: "./src",
112
111
  minUsages: 2,
113
112
  target: "all",
114
- output: "simple",
113
+ debug: false,
115
114
  tsconfig: "./tsconfig.json",
116
115
  allowedValueTypes: "all"
117
116
  };
@@ -123,15 +122,14 @@ var CliValidationError = class extends Error {
123
122
  };
124
123
  const VALID_TARGETS = [
125
124
  "all",
126
- "components",
125
+ "react-components",
127
126
  "functions"
128
127
  ];
129
- const VALID_OUTPUTS = ["simple", "verbose"];
130
128
  /** 不明なオプションの検出に使用 */
131
129
  const VALID_OPTIONS = [
132
130
  "--min",
133
131
  "--target",
134
- "--output",
132
+ "--debug",
135
133
  "--tsconfig",
136
134
  "--value-types",
137
135
  "--help"
@@ -160,11 +158,8 @@ function parseCliOptions(args) {
160
158
  const value = arg.slice(9);
161
159
  if (!VALID_TARGETS.includes(value)) throw new CliValidationError(`Invalid value for --target: "${value}" (valid values: ${VALID_TARGETS.join(", ")})`);
162
160
  result.target = value;
163
- } else if (arg.startsWith("--output=")) {
164
- const value = arg.slice(9);
165
- if (!VALID_OUTPUTS.includes(value)) throw new CliValidationError(`Invalid value for --output: "${value}" (valid values: ${VALID_OUTPUTS.join(", ")})`);
166
- result.output = value;
167
- } else if (arg.startsWith("--tsconfig=")) {
161
+ } else if (arg === "--debug") result.debug = true;
162
+ else if (arg.startsWith("--tsconfig=")) {
168
163
  const value = arg.slice(11);
169
164
  if (value === "") throw new CliValidationError("Invalid value for --tsconfig: path cannot be empty");
170
165
  result.tsconfig = value;
@@ -208,8 +203,8 @@ Usage: dittory [options] [directory]
208
203
 
209
204
  Options:
210
205
  --min=<number> Minimum usage count (default: 2)
211
- --target=<mode> Analysis target: all, components, functions (default: all)
212
- --output=<mode> Output mode: simple, verbose (default: simple)
206
+ --target=<mode> Analysis target: all, react-components, functions (default: all)
207
+ --debug Show verbose output (default: false)
213
208
  --tsconfig=<path> Path to tsconfig.json (default: ./tsconfig.json)
214
209
  --value-types=<types> Value types to detect: boolean, number, string, enum, undefined, all (default: all)
215
210
  Multiple types can be specified with comma: --value-types=boolean,string
@@ -273,8 +268,8 @@ function printStatistics(result) {
273
268
  /**
274
269
  * 解析結果を出力
275
270
  */
276
- function printAnalysisResult(result, mode) {
277
- if (mode === "verbose") printDeclarations(result.declarations);
271
+ function printAnalysisResult(result, debug) {
272
+ if (debug) printDeclarations(result.declarations);
278
273
  if (result.constantParams.isEmpty()) console.log("No arguments with constant values were found.");
279
274
  else printConstantParams(result.constantParams);
280
275
  printStatistics(result);
@@ -323,11 +318,11 @@ async function main() {
323
318
  if (error instanceof Error) exitWithError(error.message);
324
319
  throw error;
325
320
  }
326
- const { targetDir, minUsages, target, output, tsconfig, allowedValueTypes } = {
321
+ const { targetDir, minUsages, target, debug, tsconfig, allowedValueTypes } = {
327
322
  targetDir: path.resolve(cliOptions.targetDir ?? fileConfig.targetDir ?? DEFAULT_OPTIONS.targetDir),
328
323
  minUsages: cliOptions.minUsages ?? fileConfig.minUsages ?? DEFAULT_OPTIONS.minUsages,
329
324
  target: cliOptions.target ?? fileConfig.target ?? DEFAULT_OPTIONS.target,
330
- output: cliOptions.output ?? fileConfig.output ?? DEFAULT_OPTIONS.output,
325
+ debug: cliOptions.debug ?? fileConfig.debug ?? DEFAULT_OPTIONS.debug,
331
326
  tsconfig: cliOptions.tsconfig ?? fileConfig.tsconfig ?? DEFAULT_OPTIONS.tsconfig,
332
327
  allowedValueTypes: cliOptions.allowedValueTypes ?? fileConfig.allowedValueTypes ?? DEFAULT_OPTIONS.allowedValueTypes
333
328
  };
@@ -343,7 +338,7 @@ async function main() {
343
338
  if (error instanceof CliValidationError) exitWithError(error.message);
344
339
  throw error;
345
340
  }
346
- if (output === "verbose") {
341
+ if (debug) {
347
342
  console.log(`Target directory: ${targetDir}`);
348
343
  console.log(`Minimum usage count: ${minUsages}`);
349
344
  console.log(`Analysis target: ${target}\n`);
@@ -352,7 +347,7 @@ async function main() {
352
347
  const callSiteMap = new CallSiteCollector().collect(sourceFilesToAnalyze);
353
348
  const declarationsToMerge = [];
354
349
  const constantParamsToMerge = [];
355
- if (target === "all" || target === "components") {
350
+ if (target === "all" || target === "react-components") {
356
351
  const propsResult = analyzePropsCore(sourceFilesToAnalyze, {
357
352
  minUsages,
358
353
  allowedValueTypes,
@@ -373,7 +368,7 @@ async function main() {
373
368
  printAnalysisResult({
374
369
  declarations: AnalyzedDeclarations.merge(...declarationsToMerge),
375
370
  constantParams: ConstantParams.merge(...constantParamsToMerge)
376
- }, output);
371
+ }, debug);
377
372
  }
378
373
  main().catch((error) => {
379
374
  console.error(error);
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["config: unknown","VALID_TARGETS: readonly AnalyzeMode[]","VALID_OUTPUTS: readonly OutputMode[]","result: DittoryConfig","VALID_TARGETS","VALID_OUTPUTS","DEFAULT_OPTIONS: ResolvedOptions","VALID_TARGETS: readonly AnalyzeMode[]","VALID_OUTPUTS: readonly OutputMode[]","VALID_OPTIONS: readonly string[]","result: RawCliOptions","cliOptions: ReturnType<typeof parseCliOptions>","fileConfig: Awaited<ReturnType<typeof loadConfig>>","declarationsToMerge: AnalyzedDeclarations[]","constantParamsToMerge: ConstantParams[]"],"sources":["../src/cli/loadConfig.ts","../src/cli/parseCliOptions.ts","../src/output/printAnalysisResult.ts","../src/source/createFilteredSourceFiles.ts","../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport {\n VALID_VALUE_TYPES,\n type ValueType,\n} from \"@/extraction/valueTypeDetector\";\nimport type { AnalyzeMode, OutputMode } from \"./parseCliOptions\";\n\n/** コンフィグファイルの検索順序 */\nconst CONFIG_FILE_NAMES = [\n \"dittory.config.js\",\n \"dittory.config.mjs\",\n \"dittory.config.json\",\n] as const;\n\n/**\n * コンフィグファイルの設定項目\n */\nexport interface DittoryConfig {\n minUsages?: number;\n target?: AnalyzeMode;\n output?: OutputMode;\n tsconfig?: string;\n targetDir?: string;\n allowedValueTypes?: ValueType[] | \"all\";\n}\n\n/**\n * コンフィグファイルを読み込む\n *\n * 現在の作業ディレクトリから以下の順序でコンフィグファイルを探す:\n * 1. dittory.config.js\n * 2. dittory.config.mjs\n * 3. dittory.config.json\n *\n * ファイルが存在しない場合は空のオブジェクトを返す。\n *\n * @returns コンフィグオブジェクト\n * @throws {Error} コンフィグファイルの読み込みに失敗した場合\n */\nexport async function loadConfig(): Promise<DittoryConfig> {\n const cwd = process.cwd();\n\n for (const fileName of CONFIG_FILE_NAMES) {\n const configPath = path.join(cwd, fileName);\n\n if (!fs.existsSync(configPath)) {\n continue;\n }\n\n if (fileName.endsWith(\".json\")) {\n return loadJsonConfig(configPath);\n }\n return loadJsConfig(configPath);\n }\n\n return {};\n}\n\n/**\n * JSON コンフィグを読み込む\n */\nfunction loadJsonConfig(configPath: string): DittoryConfig {\n const content = fs.readFileSync(configPath, \"utf-8\");\n\n try {\n const config: unknown = JSON.parse(content);\n\n if (typeof config !== \"object\" || config === null) {\n throw new Error(`Invalid config: expected object, got ${typeof config}`);\n }\n\n return validateConfig(config as Record<string, unknown>);\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(\n `Failed to parse ${path.basename(configPath)}: ${error.message}`,\n );\n }\n throw error;\n }\n}\n\n/**\n * JS コンフィグを読み込む\n */\nasync function loadJsConfig(configPath: string): Promise<DittoryConfig> {\n try {\n // Windows 対応のため file:// URL に変換\n const fileUrl = pathToFileURL(configPath).href;\n const module = (await import(fileUrl)) as { default?: unknown };\n const config = module.default;\n\n if (typeof config !== \"object\" || config === null) {\n throw new Error(`Invalid config: expected object, got ${typeof config}`);\n }\n\n return validateConfig(config as Record<string, unknown>);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(\n `Failed to load ${path.basename(configPath)}: ${error.message}`,\n );\n }\n throw error;\n }\n}\n\nconst VALID_TARGETS: readonly AnalyzeMode[] = [\n \"all\",\n \"components\",\n \"functions\",\n];\nconst VALID_OUTPUTS: readonly OutputMode[] = [\"simple\", \"verbose\"];\n\n/**\n * コンフィグの値を検証する\n */\nfunction validateConfig(config: Record<string, unknown>): DittoryConfig {\n const result: DittoryConfig = {};\n\n if (\"minUsages\" in config) {\n if (typeof config.minUsages !== \"number\" || config.minUsages < 1) {\n throw new Error(\n `Invalid config: minUsages must be a number >= 1, got ${config.minUsages}`,\n );\n }\n result.minUsages = config.minUsages;\n }\n\n if (\"target\" in config) {\n if (!VALID_TARGETS.includes(config.target as AnalyzeMode)) {\n throw new Error(\n `Invalid config: target must be one of ${VALID_TARGETS.join(\", \")}, got ${config.target}`,\n );\n }\n result.target = config.target as AnalyzeMode;\n }\n\n if (\"output\" in config) {\n if (!VALID_OUTPUTS.includes(config.output as OutputMode)) {\n throw new Error(\n `Invalid config: output must be one of ${VALID_OUTPUTS.join(\", \")}, got ${config.output}`,\n );\n }\n result.output = config.output as OutputMode;\n }\n\n if (\"tsconfig\" in config) {\n if (typeof config.tsconfig !== \"string\" || config.tsconfig === \"\") {\n throw new Error(\n `Invalid config: tsconfig must be a non-empty string, got ${config.tsconfig}`,\n );\n }\n result.tsconfig = config.tsconfig;\n }\n\n if (\"targetDir\" in config) {\n if (typeof config.targetDir !== \"string\" || config.targetDir === \"\") {\n throw new Error(\n `Invalid config: targetDir must be a non-empty string, got ${config.targetDir}`,\n );\n }\n result.targetDir = config.targetDir;\n }\n\n if (\"valueTypes\" in config) {\n const value = config.valueTypes;\n\n if (value === \"all\") {\n result.allowedValueTypes = \"all\";\n } else if (Array.isArray(value)) {\n for (const type of value) {\n if (!VALID_VALUE_TYPES.includes(type as ValueType)) {\n throw new Error(\n `Invalid config: valueTypes contains invalid type \"${type}\" (valid values: ${VALID_VALUE_TYPES.join(\", \")}, all)`,\n );\n }\n }\n result.allowedValueTypes = value as ValueType[];\n } else {\n throw new Error(\n `Invalid config: valueTypes must be \"all\" or an array of value types, got ${value}`,\n );\n }\n }\n\n return result;\n}\n","import fs from \"node:fs\";\nimport {\n VALID_VALUE_TYPES,\n type ValueType,\n} from \"@/extraction/valueTypeDetector\";\n\nexport type AnalyzeMode = \"all\" | \"components\" | \"functions\";\nexport type OutputMode = \"simple\" | \"verbose\";\n\n/**\n * CLI で明示的に指定されたオプション(デフォルト値なし)\n */\nexport interface RawCliOptions {\n targetDir?: string;\n minUsages?: number;\n target?: AnalyzeMode;\n output?: OutputMode;\n tsconfig?: string;\n allowedValueTypes?: ValueType[];\n showHelp: boolean;\n}\n\n/**\n * 解決済みのオプション(デフォルト値適用後)\n */\nexport interface ResolvedOptions {\n targetDir: string;\n minUsages: number;\n target: AnalyzeMode;\n output: OutputMode;\n tsconfig: string;\n allowedValueTypes: ValueType[] | \"all\";\n}\n\n/** デフォルトのオプション値 */\nexport const DEFAULT_OPTIONS: ResolvedOptions = {\n targetDir: \"./src\",\n minUsages: 2,\n target: \"all\",\n output: \"simple\",\n tsconfig: \"./tsconfig.json\",\n allowedValueTypes: \"all\",\n};\n\nexport class CliValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CliValidationError\";\n }\n}\n\nconst VALID_TARGETS: readonly AnalyzeMode[] = [\n \"all\",\n \"components\",\n \"functions\",\n];\n\nconst VALID_OUTPUTS: readonly OutputMode[] = [\"simple\", \"verbose\"];\n\n/** 不明なオプションの検出に使用 */\nconst VALID_OPTIONS: readonly string[] = [\n \"--min\",\n \"--target\",\n \"--output\",\n \"--tsconfig\",\n \"--value-types\",\n \"--help\",\n];\n\n/**\n * CLIオプションをパースする\n *\n * 明示的に指定されたオプションのみを返す(デフォルト値は含まない)\n *\n * @throws {CliValidationError} オプションが無効な場合\n */\nexport function parseCliOptions(args: string[]): RawCliOptions {\n const result: RawCliOptions = { showHelp: false };\n\n for (const arg of args) {\n if (arg === \"--help\") {\n result.showHelp = true;\n continue;\n }\n\n if (arg.startsWith(\"--min=\")) {\n const valueStr = arg.slice(6);\n const value = Number.parseInt(valueStr, 10);\n\n if (valueStr === \"\" || Number.isNaN(value)) {\n throw new CliValidationError(\n `Invalid value for --min: \"${valueStr}\" (must be a number)`,\n );\n }\n if (value < 1) {\n throw new CliValidationError(`--min must be at least 1: ${value}`);\n }\n\n result.minUsages = value;\n } else if (arg.startsWith(\"--target=\")) {\n const value = arg.slice(9);\n\n if (!VALID_TARGETS.includes(value as AnalyzeMode)) {\n throw new CliValidationError(\n `Invalid value for --target: \"${value}\" (valid values: ${VALID_TARGETS.join(\n \", \",\n )})`,\n );\n }\n\n result.target = value as AnalyzeMode;\n } else if (arg.startsWith(\"--output=\")) {\n const value = arg.slice(9);\n\n if (!VALID_OUTPUTS.includes(value as OutputMode)) {\n throw new CliValidationError(\n `Invalid value for --output: \"${value}\" (valid values: ${VALID_OUTPUTS.join(\n \", \",\n )})`,\n );\n }\n\n result.output = value as OutputMode;\n } else if (arg.startsWith(\"--tsconfig=\")) {\n const value = arg.slice(11);\n\n if (value === \"\") {\n throw new CliValidationError(\n \"Invalid value for --tsconfig: path cannot be empty\",\n );\n }\n\n result.tsconfig = value;\n } else if (arg.startsWith(\"--value-types=\")) {\n const value = arg.slice(14);\n\n if (value === \"\") {\n throw new CliValidationError(\n \"Invalid value for --value-types: value cannot be empty\",\n );\n }\n\n // \"all\" の場合はデフォルト扱い(RawCliOptionsには含めない)\n if (value === \"all\") {\n continue;\n }\n\n const types = value.split(\",\").map((t) => t.trim());\n for (const type of types) {\n if (!VALID_VALUE_TYPES.includes(type as ValueType)) {\n throw new CliValidationError(\n `Invalid value type: \"${type}\" (valid values: ${VALID_VALUE_TYPES.join(\", \")}, all)`,\n );\n }\n }\n\n result.allowedValueTypes = types as ValueType[];\n } else if (arg.startsWith(\"--\")) {\n const optionName = arg.split(\"=\")[0];\n\n if (!VALID_OPTIONS.includes(optionName)) {\n throw new CliValidationError(`Unknown option: ${optionName}`);\n }\n } else {\n result.targetDir = arg;\n }\n }\n\n return result;\n}\n\n/**\n * 対象ディレクトリの存在を検証する\n *\n * @throws {CliValidationError} ディレクトリが存在しない、またはディレクトリでない場合\n */\nexport function validateTargetDir(targetDir: string): void {\n if (!fs.existsSync(targetDir)) {\n throw new CliValidationError(`Directory does not exist: ${targetDir}`);\n }\n\n const stat = fs.statSync(targetDir);\n if (!stat.isDirectory()) {\n throw new CliValidationError(`Path is not a directory: ${targetDir}`);\n }\n}\n\n/**\n * tsconfig.json の存在を検証する\n *\n * @throws {CliValidationError} ファイルが存在しない場合\n */\nexport function validateTsConfig(tsConfigPath: string): void {\n if (!fs.existsSync(tsConfigPath)) {\n throw new CliValidationError(`tsconfig not found: ${tsConfigPath}`);\n }\n}\n\n/**\n * ヘルプメッセージを取得する\n */\nexport function getHelpMessage(): string {\n return `\nUsage: dittory [options] [directory]\n\nOptions:\n --min=<number> Minimum usage count (default: 2)\n --target=<mode> Analysis target: all, components, functions (default: all)\n --output=<mode> Output mode: simple, verbose (default: simple)\n --tsconfig=<path> Path to tsconfig.json (default: ./tsconfig.json)\n --value-types=<types> Value types to detect: boolean, number, string, enum, undefined, all (default: all)\n Multiple types can be specified with comma: --value-types=boolean,string\n --help Show this help message\n\nArguments:\n directory Target directory to analyze (default: ./src)\n`;\n}\n","import path from \"node:path\";\nimport type { OutputMode } from \"@/cli/parseCliOptions\";\nimport type { AnalysisResult } from \"@/domain/analysisResult\";\nimport type { AnalyzedDeclarations } from \"@/domain/analyzedDeclarations\";\nimport type { ConstantParams } from \"@/domain/constantParams\";\n\nfunction bold(text: string): string {\n return `\\x1b[1m${text}\\x1b[0m`;\n}\n\nfunction green(text: string): string {\n return `\\x1b[32m${text}\\x1b[0m`;\n}\n\n/**\n * 分析対象の一覧を出力\n */\nfunction printDeclarations(declarations: AnalyzedDeclarations): void {\n const lines = [\n \"Collecting exported functions...\",\n ` → Found ${declarations.length} function(s)`,\n ...declarations.map(\n (decl) =>\n ` - ${bold(green(decl.name))} (${path.relative(\n process.cwd(),\n decl.sourceFilePath,\n )})`,\n ),\n \"\",\n ];\n console.log(lines.join(\"\\n\"));\n}\n\n/**\n * 常に同じ値が渡されている引数を出力\n */\nfunction printConstantParams(constantParams: ConstantParams): void {\n if (constantParams.isEmpty()) {\n return;\n }\n\n const grouped = constantParams.groupByDeclaration();\n\n for (const constantParamGroup of grouped) {\n const relativePath = path.relative(\n process.cwd(),\n constantParamGroup.declarationSourceFile,\n );\n const usageCount = constantParamGroup.params[0]?.usageCount ?? 0;\n // 使用箇所は全パラメータで同じなので、最初のパラメータから取得\n const usages = constantParamGroup.params[0]?.usages ?? [];\n\n console.log(\n `${bold(green(constantParamGroup.declarationName))} ${relativePath}:${constantParamGroup.declarationLine}`,\n );\n console.log(\"Constant Arguments:\");\n\n for (const param of constantParamGroup.params) {\n console.log(` - ${param.paramName} = ${param.value.outputString()}`);\n }\n\n console.log(`Usages (${usageCount}):`);\n for (const usage of usages) {\n const usagePath = path.relative(process.cwd(), usage.usageFilePath);\n console.log(` - ${usagePath}:${usage.usageLine}`);\n }\n\n console.log(\"\\n\");\n }\n}\n\n/**\n * 統計情報を出力\n */\nfunction printStatistics(result: AnalysisResult): void {\n const totalFunctions = result.declarations.length;\n const functionsWithConstants =\n result.constantParams.groupByDeclaration().length;\n\n console.log(\"---\");\n console.log(\n `Found ${functionsWithConstants} function(s) with constant arguments out of ${totalFunctions} function(s).`,\n );\n}\n\n/**\n * 解析結果を出力\n */\nexport function printAnalysisResult(\n result: AnalysisResult,\n mode: OutputMode,\n): void {\n if (mode === \"verbose\") {\n printDeclarations(result.declarations);\n }\n\n if (result.constantParams.isEmpty()) {\n console.log(\"No arguments with constant values were found.\");\n } else {\n printConstantParams(result.constantParams);\n }\n\n printStatistics(result);\n}\n","import path from \"node:path\";\nimport { Project, type SourceFile } from \"ts-morph\";\nimport type { FileFilter } from \"@/domain/analyzerOptions\";\nimport { isTestOrStorybookFile } from \"@/source/fileFilters\";\n\nexport interface CreateFilteredSourceFilesOptions {\n shouldExcludeFile?: FileFilter;\n tsConfigFilePath?: string;\n}\n\n/**\n * プロジェクトを初期化し、フィルタリングされたソースファイルを取得する\n */\nexport function createFilteredSourceFiles(\n targetDir: string,\n options: CreateFilteredSourceFilesOptions = {},\n): SourceFile[] {\n const {\n shouldExcludeFile = isTestOrStorybookFile,\n tsConfigFilePath = path.join(process.cwd(), \"tsconfig.json\"),\n } = options;\n\n // プロジェクトを初期化\n const project = new Project({\n tsConfigFilePath,\n skipAddingFilesFromTsConfig: true,\n });\n\n // 対象ディレクトリのファイルを追加\n project.addSourceFilesAtPaths(`${targetDir}/**/*.{ts,tsx,js,jsx}`);\n\n // ファイルをフィルタリング\n const allSourceFiles = project.getSourceFiles();\n const sourceFilesToAnalyze = allSourceFiles.filter(\n (sourceFile) => !shouldExcludeFile(sourceFile.getFilePath()),\n );\n\n return sourceFilesToAnalyze;\n}\n","#!/usr/bin/env node\nimport path from \"node:path\";\nimport { analyzeFunctionsCore } from \"@/analyzeFunctions\";\nimport { analyzePropsCore } from \"@/analyzeProps\";\nimport { loadConfig } from \"@/cli/loadConfig\";\nimport {\n CliValidationError,\n DEFAULT_OPTIONS,\n getHelpMessage,\n parseCliOptions,\n type ResolvedOptions,\n validateTargetDir,\n validateTsConfig,\n} from \"@/cli/parseCliOptions\";\nimport type { AnalysisResult } from \"@/domain/analysisResult\";\nimport { AnalyzedDeclarations } from \"@/domain/analyzedDeclarations\";\nimport { ConstantParams } from \"@/domain/constantParams\";\nimport { CallSiteCollector } from \"@/extraction/callSiteCollector\";\nimport { printAnalysisResult } from \"@/output/printAnalysisResult\";\nimport { createFilteredSourceFiles } from \"@/source/createFilteredSourceFiles\";\n\n/**\n * エラーメッセージを表示してプロセスを終了する\n */\nfunction exitWithError(message: string): never {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\nasync function main(): Promise<void> {\n // CLI オプションをパース\n let cliOptions: ReturnType<typeof parseCliOptions>;\n try {\n cliOptions = parseCliOptions(process.argv.slice(2));\n } catch (error) {\n if (error instanceof CliValidationError) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n if (cliOptions.showHelp) {\n console.log(getHelpMessage());\n process.exit(0);\n }\n\n // コンフィグファイルを読み込む\n let fileConfig: Awaited<ReturnType<typeof loadConfig>>;\n try {\n fileConfig = await loadConfig();\n } catch (error) {\n if (error instanceof Error) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n // オプションをマージ: CLI > コンフィグ > デフォルト\n const options: ResolvedOptions = {\n targetDir: path.resolve(\n cliOptions.targetDir ?? fileConfig.targetDir ?? DEFAULT_OPTIONS.targetDir,\n ),\n minUsages:\n cliOptions.minUsages ?? fileConfig.minUsages ?? DEFAULT_OPTIONS.minUsages,\n target: cliOptions.target ?? fileConfig.target ?? DEFAULT_OPTIONS.target,\n output: cliOptions.output ?? fileConfig.output ?? DEFAULT_OPTIONS.output,\n tsconfig:\n cliOptions.tsconfig ?? fileConfig.tsconfig ?? DEFAULT_OPTIONS.tsconfig,\n allowedValueTypes:\n cliOptions.allowedValueTypes ??\n fileConfig.allowedValueTypes ??\n DEFAULT_OPTIONS.allowedValueTypes,\n };\n\n const { targetDir, minUsages, target, output, tsconfig, allowedValueTypes } =\n options;\n\n // 対象ディレクトリの存在を検証\n try {\n validateTargetDir(targetDir);\n } catch (error) {\n if (error instanceof CliValidationError) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n // tsconfig.json の存在を検証\n try {\n validateTsConfig(tsconfig);\n } catch (error) {\n if (error instanceof CliValidationError) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n if (output === \"verbose\") {\n console.log(`Target directory: ${targetDir}`);\n console.log(`Minimum usage count: ${minUsages}`);\n console.log(`Analysis target: ${target}\\n`);\n }\n\n const sourceFilesToAnalyze = createFilteredSourceFiles(targetDir, {\n tsConfigFilePath: tsconfig,\n });\n\n // 呼び出し情報を事前収集(パラメータ経由で渡された値を解決するために使用)\n const callSiteMap = new CallSiteCollector().collect(sourceFilesToAnalyze);\n\n // 各解析結果を収集\n const declarationsToMerge: AnalyzedDeclarations[] = [];\n const constantParamsToMerge: ConstantParams[] = [];\n\n if (target === \"all\" || target === \"components\") {\n const propsResult = analyzePropsCore(sourceFilesToAnalyze, {\n minUsages,\n allowedValueTypes,\n callSiteMap,\n });\n declarationsToMerge.push(propsResult.declarations);\n constantParamsToMerge.push(propsResult.constantParams);\n }\n\n if (target === \"all\" || target === \"functions\") {\n const functionsResult = analyzeFunctionsCore(sourceFilesToAnalyze, {\n minUsages,\n allowedValueTypes,\n callSiteMap,\n });\n declarationsToMerge.push(functionsResult.declarations);\n constantParamsToMerge.push(functionsResult.constantParams);\n }\n\n const result: AnalysisResult = {\n declarations: AnalyzedDeclarations.merge(...declarationsToMerge),\n constantParams: ConstantParams.merge(...constantParamsToMerge),\n };\n\n printAnalysisResult(result, output);\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["config: unknown","VALID_TARGETS: readonly AnalyzeMode[]","result: DittoryConfig","VALID_TARGETS","DEFAULT_OPTIONS: ResolvedOptions","VALID_TARGETS: readonly AnalyzeMode[]","VALID_OPTIONS: readonly string[]","result: RawCliOptions","cliOptions: ReturnType<typeof parseCliOptions>","fileConfig: Awaited<ReturnType<typeof loadConfig>>","declarationsToMerge: AnalyzedDeclarations[]","constantParamsToMerge: ConstantParams[]"],"sources":["../src/cli/loadConfig.ts","../src/cli/parseCliOptions.ts","../src/output/printAnalysisResult.ts","../src/source/createFilteredSourceFiles.ts","../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport {\n VALID_VALUE_TYPES,\n type ValueType,\n} from \"@/extraction/valueTypeDetector\";\nimport type { AnalyzeMode } from \"./parseCliOptions\";\n\n/** コンフィグファイルの検索順序 */\nconst CONFIG_FILE_NAMES = [\n \"dittory.config.js\",\n \"dittory.config.mjs\",\n \"dittory.config.json\",\n] as const;\n\n/**\n * コンフィグファイルの設定項目\n */\nexport interface DittoryConfig {\n minUsages?: number;\n target?: AnalyzeMode;\n debug?: boolean;\n tsconfig?: string;\n targetDir?: string;\n allowedValueTypes?: ValueType[] | \"all\";\n}\n\n/**\n * コンフィグファイルを読み込む\n *\n * 現在の作業ディレクトリから以下の順序でコンフィグファイルを探す:\n * 1. dittory.config.js\n * 2. dittory.config.mjs\n * 3. dittory.config.json\n *\n * ファイルが存在しない場合は空のオブジェクトを返す。\n *\n * @returns コンフィグオブジェクト\n * @throws {Error} コンフィグファイルの読み込みに失敗した場合\n */\nexport async function loadConfig(): Promise<DittoryConfig> {\n const cwd = process.cwd();\n\n for (const fileName of CONFIG_FILE_NAMES) {\n const configPath = path.join(cwd, fileName);\n\n if (!fs.existsSync(configPath)) {\n continue;\n }\n\n if (fileName.endsWith(\".json\")) {\n return loadJsonConfig(configPath);\n }\n return loadJsConfig(configPath);\n }\n\n return {};\n}\n\n/**\n * JSON コンフィグを読み込む\n */\nfunction loadJsonConfig(configPath: string): DittoryConfig {\n const content = fs.readFileSync(configPath, \"utf-8\");\n\n try {\n const config: unknown = JSON.parse(content);\n\n if (typeof config !== \"object\" || config === null) {\n throw new Error(`Invalid config: expected object, got ${typeof config}`);\n }\n\n return validateConfig(config as Record<string, unknown>);\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(\n `Failed to parse ${path.basename(configPath)}: ${error.message}`,\n );\n }\n throw error;\n }\n}\n\n/**\n * JS コンフィグを読み込む\n */\nasync function loadJsConfig(configPath: string): Promise<DittoryConfig> {\n try {\n // Windows 対応のため file:// URL に変換\n const fileUrl = pathToFileURL(configPath).href;\n const module = (await import(fileUrl)) as { default?: unknown };\n const config = module.default;\n\n if (typeof config !== \"object\" || config === null) {\n throw new Error(`Invalid config: expected object, got ${typeof config}`);\n }\n\n return validateConfig(config as Record<string, unknown>);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(\n `Failed to load ${path.basename(configPath)}: ${error.message}`,\n );\n }\n throw error;\n }\n}\n\nconst VALID_TARGETS: readonly AnalyzeMode[] = [\n \"all\",\n \"react-components\",\n \"functions\",\n];\n\n/**\n * コンフィグの値を検証する\n */\nfunction validateConfig(config: Record<string, unknown>): DittoryConfig {\n const result: DittoryConfig = {};\n\n if (\"minUsages\" in config) {\n if (typeof config.minUsages !== \"number\" || config.minUsages < 1) {\n throw new Error(\n `Invalid config: minUsages must be a number >= 1, got ${config.minUsages}`,\n );\n }\n result.minUsages = config.minUsages;\n }\n\n if (\"target\" in config) {\n if (!VALID_TARGETS.includes(config.target as AnalyzeMode)) {\n throw new Error(\n `Invalid config: target must be one of ${VALID_TARGETS.join(\", \")}, got ${config.target}`,\n );\n }\n result.target = config.target as AnalyzeMode;\n }\n\n if (\"debug\" in config) {\n if (typeof config.debug !== \"boolean\") {\n throw new Error(\n `Invalid config: debug must be a boolean, got ${typeof config.debug}`,\n );\n }\n result.debug = config.debug;\n }\n\n if (\"tsconfig\" in config) {\n if (typeof config.tsconfig !== \"string\" || config.tsconfig === \"\") {\n throw new Error(\n `Invalid config: tsconfig must be a non-empty string, got ${config.tsconfig}`,\n );\n }\n result.tsconfig = config.tsconfig;\n }\n\n if (\"targetDir\" in config) {\n if (typeof config.targetDir !== \"string\" || config.targetDir === \"\") {\n throw new Error(\n `Invalid config: targetDir must be a non-empty string, got ${config.targetDir}`,\n );\n }\n result.targetDir = config.targetDir;\n }\n\n if (\"valueTypes\" in config) {\n const value = config.valueTypes;\n\n if (value === \"all\") {\n result.allowedValueTypes = \"all\";\n } else if (Array.isArray(value)) {\n for (const type of value) {\n if (!VALID_VALUE_TYPES.includes(type as ValueType)) {\n throw new Error(\n `Invalid config: valueTypes contains invalid type \"${type}\" (valid values: ${VALID_VALUE_TYPES.join(\", \")}, all)`,\n );\n }\n }\n result.allowedValueTypes = value as ValueType[];\n } else {\n throw new Error(\n `Invalid config: valueTypes must be \"all\" or an array of value types, got ${value}`,\n );\n }\n }\n\n return result;\n}\n","import fs from \"node:fs\";\nimport {\n VALID_VALUE_TYPES,\n type ValueType,\n} from \"@/extraction/valueTypeDetector\";\n\nexport type AnalyzeMode = \"all\" | \"react-components\" | \"functions\";\n\n/**\n * CLI で明示的に指定されたオプション(デフォルト値なし)\n */\nexport interface RawCliOptions {\n targetDir?: string;\n minUsages?: number;\n target?: AnalyzeMode;\n debug?: boolean;\n tsconfig?: string;\n allowedValueTypes?: ValueType[];\n showHelp: boolean;\n}\n\n/**\n * 解決済みのオプション(デフォルト値適用後)\n */\nexport interface ResolvedOptions {\n targetDir: string;\n minUsages: number;\n target: AnalyzeMode;\n debug: boolean;\n tsconfig: string;\n allowedValueTypes: ValueType[] | \"all\";\n}\n\n/** デフォルトのオプション値 */\nexport const DEFAULT_OPTIONS: ResolvedOptions = {\n targetDir: \"./src\",\n minUsages: 2,\n target: \"all\",\n debug: false,\n tsconfig: \"./tsconfig.json\",\n allowedValueTypes: \"all\",\n};\n\nexport class CliValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CliValidationError\";\n }\n}\n\nconst VALID_TARGETS: readonly AnalyzeMode[] = [\n \"all\",\n \"react-components\",\n \"functions\",\n];\n\n/** 不明なオプションの検出に使用 */\nconst VALID_OPTIONS: readonly string[] = [\n \"--min\",\n \"--target\",\n \"--debug\",\n \"--tsconfig\",\n \"--value-types\",\n \"--help\",\n];\n\n/**\n * CLIオプションをパースする\n *\n * 明示的に指定されたオプションのみを返す(デフォルト値は含まない)\n *\n * @throws {CliValidationError} オプションが無効な場合\n */\nexport function parseCliOptions(args: string[]): RawCliOptions {\n const result: RawCliOptions = { showHelp: false };\n\n for (const arg of args) {\n if (arg === \"--help\") {\n result.showHelp = true;\n continue;\n }\n\n if (arg.startsWith(\"--min=\")) {\n const valueStr = arg.slice(6);\n const value = Number.parseInt(valueStr, 10);\n\n if (valueStr === \"\" || Number.isNaN(value)) {\n throw new CliValidationError(\n `Invalid value for --min: \"${valueStr}\" (must be a number)`,\n );\n }\n if (value < 1) {\n throw new CliValidationError(`--min must be at least 1: ${value}`);\n }\n\n result.minUsages = value;\n } else if (arg.startsWith(\"--target=\")) {\n const value = arg.slice(9);\n\n if (!VALID_TARGETS.includes(value as AnalyzeMode)) {\n throw new CliValidationError(\n `Invalid value for --target: \"${value}\" (valid values: ${VALID_TARGETS.join(\n \", \",\n )})`,\n );\n }\n\n result.target = value as AnalyzeMode;\n } else if (arg === \"--debug\") {\n result.debug = true;\n } else if (arg.startsWith(\"--tsconfig=\")) {\n const value = arg.slice(11);\n\n if (value === \"\") {\n throw new CliValidationError(\n \"Invalid value for --tsconfig: path cannot be empty\",\n );\n }\n\n result.tsconfig = value;\n } else if (arg.startsWith(\"--value-types=\")) {\n const value = arg.slice(14);\n\n if (value === \"\") {\n throw new CliValidationError(\n \"Invalid value for --value-types: value cannot be empty\",\n );\n }\n\n // \"all\" の場合はデフォルト扱い(RawCliOptionsには含めない)\n if (value === \"all\") {\n continue;\n }\n\n const types = value.split(\",\").map((t) => t.trim());\n for (const type of types) {\n if (!VALID_VALUE_TYPES.includes(type as ValueType)) {\n throw new CliValidationError(\n `Invalid value type: \"${type}\" (valid values: ${VALID_VALUE_TYPES.join(\", \")}, all)`,\n );\n }\n }\n\n result.allowedValueTypes = types as ValueType[];\n } else if (arg.startsWith(\"--\")) {\n const optionName = arg.split(\"=\")[0];\n\n if (!VALID_OPTIONS.includes(optionName)) {\n throw new CliValidationError(`Unknown option: ${optionName}`);\n }\n } else {\n result.targetDir = arg;\n }\n }\n\n return result;\n}\n\n/**\n * 対象ディレクトリの存在を検証する\n *\n * @throws {CliValidationError} ディレクトリが存在しない、またはディレクトリでない場合\n */\nexport function validateTargetDir(targetDir: string): void {\n if (!fs.existsSync(targetDir)) {\n throw new CliValidationError(`Directory does not exist: ${targetDir}`);\n }\n\n const stat = fs.statSync(targetDir);\n if (!stat.isDirectory()) {\n throw new CliValidationError(`Path is not a directory: ${targetDir}`);\n }\n}\n\n/**\n * tsconfig.json の存在を検証する\n *\n * @throws {CliValidationError} ファイルが存在しない場合\n */\nexport function validateTsConfig(tsConfigPath: string): void {\n if (!fs.existsSync(tsConfigPath)) {\n throw new CliValidationError(`tsconfig not found: ${tsConfigPath}`);\n }\n}\n\n/**\n * ヘルプメッセージを取得する\n */\nexport function getHelpMessage(): string {\n return `\nUsage: dittory [options] [directory]\n\nOptions:\n --min=<number> Minimum usage count (default: 2)\n --target=<mode> Analysis target: all, react-components, functions (default: all)\n --debug Show verbose output (default: false)\n --tsconfig=<path> Path to tsconfig.json (default: ./tsconfig.json)\n --value-types=<types> Value types to detect: boolean, number, string, enum, undefined, all (default: all)\n Multiple types can be specified with comma: --value-types=boolean,string\n --help Show this help message\n\nArguments:\n directory Target directory to analyze (default: ./src)\n`;\n}\n","import path from \"node:path\";\nimport type { AnalysisResult } from \"@/domain/analysisResult\";\nimport type { AnalyzedDeclarations } from \"@/domain/analyzedDeclarations\";\nimport type { ConstantParams } from \"@/domain/constantParams\";\n\nfunction bold(text: string): string {\n return `\\x1b[1m${text}\\x1b[0m`;\n}\n\nfunction green(text: string): string {\n return `\\x1b[32m${text}\\x1b[0m`;\n}\n\n/**\n * 分析対象の一覧を出力\n */\nfunction printDeclarations(declarations: AnalyzedDeclarations): void {\n const lines = [\n \"Collecting exported functions...\",\n ` → Found ${declarations.length} function(s)`,\n ...declarations.map(\n (decl) =>\n ` - ${bold(green(decl.name))} (${path.relative(\n process.cwd(),\n decl.sourceFilePath,\n )})`,\n ),\n \"\",\n ];\n console.log(lines.join(\"\\n\"));\n}\n\n/**\n * 常に同じ値が渡されている引数を出力\n */\nfunction printConstantParams(constantParams: ConstantParams): void {\n if (constantParams.isEmpty()) {\n return;\n }\n\n const grouped = constantParams.groupByDeclaration();\n\n for (const constantParamGroup of grouped) {\n const relativePath = path.relative(\n process.cwd(),\n constantParamGroup.declarationSourceFile,\n );\n const usageCount = constantParamGroup.params[0]?.usageCount ?? 0;\n // 使用箇所は全パラメータで同じなので、最初のパラメータから取得\n const usages = constantParamGroup.params[0]?.usages ?? [];\n\n console.log(\n `${bold(green(constantParamGroup.declarationName))} ${relativePath}:${constantParamGroup.declarationLine}`,\n );\n console.log(\"Constant Arguments:\");\n\n for (const param of constantParamGroup.params) {\n console.log(` - ${param.paramName} = ${param.value.outputString()}`);\n }\n\n console.log(`Usages (${usageCount}):`);\n for (const usage of usages) {\n const usagePath = path.relative(process.cwd(), usage.usageFilePath);\n console.log(` - ${usagePath}:${usage.usageLine}`);\n }\n\n console.log(\"\\n\");\n }\n}\n\n/**\n * 統計情報を出力\n */\nfunction printStatistics(result: AnalysisResult): void {\n const totalFunctions = result.declarations.length;\n const functionsWithConstants =\n result.constantParams.groupByDeclaration().length;\n\n console.log(\"---\");\n console.log(\n `Found ${functionsWithConstants} function(s) with constant arguments out of ${totalFunctions} function(s).`,\n );\n}\n\n/**\n * 解析結果を出力\n */\nexport function printAnalysisResult(\n result: AnalysisResult,\n debug: boolean,\n): void {\n if (debug) {\n printDeclarations(result.declarations);\n }\n\n if (result.constantParams.isEmpty()) {\n console.log(\"No arguments with constant values were found.\");\n } else {\n printConstantParams(result.constantParams);\n }\n\n printStatistics(result);\n}\n","import path from \"node:path\";\nimport { Project, type SourceFile } from \"ts-morph\";\nimport type { FileFilter } from \"@/domain/analyzerOptions\";\nimport { isTestOrStorybookFile } from \"@/source/fileFilters\";\n\nexport interface CreateFilteredSourceFilesOptions {\n shouldExcludeFile?: FileFilter;\n tsConfigFilePath?: string;\n}\n\n/**\n * プロジェクトを初期化し、フィルタリングされたソースファイルを取得する\n */\nexport function createFilteredSourceFiles(\n targetDir: string,\n options: CreateFilteredSourceFilesOptions = {},\n): SourceFile[] {\n const {\n shouldExcludeFile = isTestOrStorybookFile,\n tsConfigFilePath = path.join(process.cwd(), \"tsconfig.json\"),\n } = options;\n\n // プロジェクトを初期化\n const project = new Project({\n tsConfigFilePath,\n skipAddingFilesFromTsConfig: true,\n });\n\n // 対象ディレクトリのファイルを追加\n project.addSourceFilesAtPaths(`${targetDir}/**/*.{ts,tsx,js,jsx}`);\n\n // ファイルをフィルタリング\n const allSourceFiles = project.getSourceFiles();\n const sourceFilesToAnalyze = allSourceFiles.filter(\n (sourceFile) => !shouldExcludeFile(sourceFile.getFilePath()),\n );\n\n return sourceFilesToAnalyze;\n}\n","#!/usr/bin/env node\nimport path from \"node:path\";\nimport { analyzeFunctionsCore } from \"@/analyzeFunctions\";\nimport { analyzePropsCore } from \"@/analyzeProps\";\nimport { loadConfig } from \"@/cli/loadConfig\";\nimport {\n CliValidationError,\n DEFAULT_OPTIONS,\n getHelpMessage,\n parseCliOptions,\n type ResolvedOptions,\n validateTargetDir,\n validateTsConfig,\n} from \"@/cli/parseCliOptions\";\nimport type { AnalysisResult } from \"@/domain/analysisResult\";\nimport { AnalyzedDeclarations } from \"@/domain/analyzedDeclarations\";\nimport { ConstantParams } from \"@/domain/constantParams\";\nimport { CallSiteCollector } from \"@/extraction/callSiteCollector\";\nimport { printAnalysisResult } from \"@/output/printAnalysisResult\";\nimport { createFilteredSourceFiles } from \"@/source/createFilteredSourceFiles\";\n\n/**\n * エラーメッセージを表示してプロセスを終了する\n */\nfunction exitWithError(message: string): never {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\nasync function main(): Promise<void> {\n // CLI オプションをパース\n let cliOptions: ReturnType<typeof parseCliOptions>;\n try {\n cliOptions = parseCliOptions(process.argv.slice(2));\n } catch (error) {\n if (error instanceof CliValidationError) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n if (cliOptions.showHelp) {\n console.log(getHelpMessage());\n process.exit(0);\n }\n\n // コンフィグファイルを読み込む\n let fileConfig: Awaited<ReturnType<typeof loadConfig>>;\n try {\n fileConfig = await loadConfig();\n } catch (error) {\n if (error instanceof Error) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n // オプションをマージ: CLI > コンフィグ > デフォルト\n const options: ResolvedOptions = {\n targetDir: path.resolve(\n cliOptions.targetDir ?? fileConfig.targetDir ?? DEFAULT_OPTIONS.targetDir,\n ),\n minUsages:\n cliOptions.minUsages ?? fileConfig.minUsages ?? DEFAULT_OPTIONS.minUsages,\n target: cliOptions.target ?? fileConfig.target ?? DEFAULT_OPTIONS.target,\n debug: cliOptions.debug ?? fileConfig.debug ?? DEFAULT_OPTIONS.debug,\n tsconfig:\n cliOptions.tsconfig ?? fileConfig.tsconfig ?? DEFAULT_OPTIONS.tsconfig,\n allowedValueTypes:\n cliOptions.allowedValueTypes ??\n fileConfig.allowedValueTypes ??\n DEFAULT_OPTIONS.allowedValueTypes,\n };\n\n const { targetDir, minUsages, target, debug, tsconfig, allowedValueTypes } =\n options;\n\n // 対象ディレクトリの存在を検証\n try {\n validateTargetDir(targetDir);\n } catch (error) {\n if (error instanceof CliValidationError) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n // tsconfig.json の存在を検証\n try {\n validateTsConfig(tsconfig);\n } catch (error) {\n if (error instanceof CliValidationError) {\n exitWithError(error.message);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`Target directory: ${targetDir}`);\n console.log(`Minimum usage count: ${minUsages}`);\n console.log(`Analysis target: ${target}\\n`);\n }\n\n const sourceFilesToAnalyze = createFilteredSourceFiles(targetDir, {\n tsConfigFilePath: tsconfig,\n });\n\n // 呼び出し情報を事前収集(パラメータ経由で渡された値を解決するために使用)\n const callSiteMap = new CallSiteCollector().collect(sourceFilesToAnalyze);\n\n // 各解析結果を収集\n const declarationsToMerge: AnalyzedDeclarations[] = [];\n const constantParamsToMerge: ConstantParams[] = [];\n\n if (target === \"all\" || target === \"react-components\") {\n const propsResult = analyzePropsCore(sourceFilesToAnalyze, {\n minUsages,\n allowedValueTypes,\n callSiteMap,\n });\n declarationsToMerge.push(propsResult.declarations);\n constantParamsToMerge.push(propsResult.constantParams);\n }\n\n if (target === \"all\" || target === \"functions\") {\n const functionsResult = analyzeFunctionsCore(sourceFilesToAnalyze, {\n minUsages,\n allowedValueTypes,\n callSiteMap,\n });\n declarationsToMerge.push(functionsResult.declarations);\n constantParamsToMerge.push(functionsResult.constantParams);\n }\n\n const result: AnalysisResult = {\n declarations: AnalyzedDeclarations.merge(...declarationsToMerge),\n constantParams: ConstantParams.merge(...constantParamsToMerge),\n };\n\n printAnalysisResult(result, debug);\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -317,8 +317,7 @@ interface AnalyzePropsOptions {
317
317
  declare function analyzePropsCore(sourceFiles: SourceFile[], options: AnalyzePropsOptions): AnalysisResult;
318
318
  //#endregion
319
319
  //#region src/cli/parseCliOptions.d.ts
320
- type AnalyzeMode = "all" | "components" | "functions";
321
- type OutputMode = "simple" | "verbose";
320
+ type AnalyzeMode = "all" | "react-components" | "functions";
322
321
  //#endregion
323
322
  //#region src/cli/loadConfig.d.ts
324
323
  /**
@@ -327,7 +326,7 @@ type OutputMode = "simple" | "verbose";
327
326
  interface DittoryConfig {
328
327
  minUsages?: number;
329
328
  target?: AnalyzeMode;
330
- output?: OutputMode;
329
+ debug?: boolean;
331
330
  tsconfig?: string;
332
331
  targetDir?: string;
333
332
  allowedValueTypes?: ValueType[] | "all";
@@ -377,5 +376,5 @@ declare class CallSiteCollector {
377
376
  private getDeclaration;
378
377
  }
379
378
  //#endregion
380
- export { type AnalysisResult, type AnalyzeMode, type AnalyzedDeclaration, CallSiteCollector, CallSiteMap, type ConstantParam, type Definition, type DittoryConfig, type FileFilter, type OutputMode, type Usage, analyzeFunctionsCore, analyzePropsCore };
379
+ export { type AnalysisResult, type AnalyzeMode, type AnalyzedDeclaration, CallSiteCollector, CallSiteMap, type ConstantParam, type Definition, type DittoryConfig, type FileFilter, type Usage, analyzeFunctionsCore, analyzePropsCore };
381
380
  //# sourceMappingURL=index.d.mts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dittory",
3
- "version": "0.0.5",
4
- "description": "A static analysis CLI that detects parameters that always receive the same value in React components and functions",
3
+ "version": "1.0.0",
4
+ "description": "A static analysis CLI for TypeScript projects that detects parameters that always receive the same value",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.mts",