semantic-complexity 0.0.7-741984fb → 0.0.15-29c6b296

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.
Files changed (163) hide show
  1. package/dist/__tests__/analyzers.test.d.ts +5 -0
  2. package/dist/__tests__/analyzers.test.d.ts.map +1 -0
  3. package/dist/__tests__/analyzers.test.js +357 -0
  4. package/dist/__tests__/analyzers.test.js.map +1 -0
  5. package/dist/__tests__/gate.test.d.ts +5 -0
  6. package/dist/__tests__/gate.test.d.ts.map +1 -0
  7. package/dist/__tests__/gate.test.js +140 -0
  8. package/dist/__tests__/gate.test.js.map +1 -0
  9. package/dist/analyzers/bread.d.ts +63 -0
  10. package/dist/analyzers/bread.d.ts.map +1 -0
  11. package/dist/analyzers/bread.js +415 -0
  12. package/dist/analyzers/bread.js.map +1 -0
  13. package/dist/analyzers/cheese.d.ts +111 -0
  14. package/dist/analyzers/cheese.d.ts.map +1 -0
  15. package/dist/analyzers/cheese.js +881 -0
  16. package/dist/analyzers/cheese.js.map +1 -0
  17. package/dist/analyzers/ham.d.ts +33 -0
  18. package/dist/analyzers/ham.d.ts.map +1 -0
  19. package/dist/analyzers/ham.js +264 -0
  20. package/dist/analyzers/ham.js.map +1 -0
  21. package/dist/analyzers/index.d.ts +7 -0
  22. package/dist/analyzers/index.d.ts.map +1 -0
  23. package/dist/analyzers/index.js +7 -0
  24. package/dist/analyzers/index.js.map +1 -0
  25. package/dist/budget/index.d.ts +50 -0
  26. package/dist/budget/index.d.ts.map +1 -0
  27. package/dist/budget/index.js +138 -0
  28. package/dist/budget/index.js.map +1 -0
  29. package/dist/cli/index.d.ts +6 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +103 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/gate/index.d.ts +31 -0
  34. package/dist/gate/index.d.ts.map +1 -0
  35. package/dist/gate/index.js +117 -0
  36. package/dist/gate/index.js.map +1 -0
  37. package/dist/gate/waiver.d.ts +83 -0
  38. package/dist/gate/waiver.d.ts.map +1 -0
  39. package/dist/gate/waiver.js +425 -0
  40. package/dist/gate/waiver.js.map +1 -0
  41. package/dist/index.d.ts +8 -34
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +8 -49
  44. package/dist/index.js.map +1 -1
  45. package/dist/mcp/index.d.ts +5 -0
  46. package/dist/mcp/index.d.ts.map +1 -0
  47. package/dist/mcp/index.js +351 -0
  48. package/dist/mcp/index.js.map +1 -0
  49. package/dist/recommend/index.d.ts +32 -0
  50. package/dist/recommend/index.d.ts.map +1 -0
  51. package/dist/recommend/index.js +169 -0
  52. package/dist/recommend/index.js.map +1 -0
  53. package/dist/simplex/index.d.ts +26 -0
  54. package/dist/simplex/index.d.ts.map +1 -0
  55. package/dist/simplex/index.js +100 -0
  56. package/dist/simplex/index.js.map +1 -0
  57. package/dist/types/index.d.ts +39 -0
  58. package/dist/types/index.d.ts.map +1 -0
  59. package/dist/types/index.js +5 -0
  60. package/dist/types/index.js.map +1 -0
  61. package/package.json +27 -52
  62. package/LICENSE +0 -21
  63. package/README.md +0 -72
  64. package/dist/ast/index.d.ts +0 -2
  65. package/dist/ast/index.d.ts.map +0 -1
  66. package/dist/ast/index.js +0 -2
  67. package/dist/ast/index.js.map +0 -1
  68. package/dist/ast/parser.d.ts +0 -27
  69. package/dist/ast/parser.d.ts.map +0 -1
  70. package/dist/ast/parser.js +0 -273
  71. package/dist/ast/parser.js.map +0 -1
  72. package/dist/canonical/convergence.d.ts +0 -56
  73. package/dist/canonical/convergence.d.ts.map +0 -1
  74. package/dist/canonical/convergence.js +0 -149
  75. package/dist/canonical/convergence.js.map +0 -1
  76. package/dist/canonical/index.d.ts +0 -11
  77. package/dist/canonical/index.d.ts.map +0 -1
  78. package/dist/canonical/index.js +0 -11
  79. package/dist/canonical/index.js.map +0 -1
  80. package/dist/canonical/profiles.d.ts +0 -40
  81. package/dist/canonical/profiles.d.ts.map +0 -1
  82. package/dist/canonical/profiles.js +0 -182
  83. package/dist/canonical/profiles.js.map +0 -1
  84. package/dist/canonical/types.d.ts +0 -124
  85. package/dist/canonical/types.d.ts.map +0 -1
  86. package/dist/canonical/types.js +0 -46
  87. package/dist/canonical/types.js.map +0 -1
  88. package/dist/compare.d.ts +0 -31
  89. package/dist/compare.d.ts.map +0 -1
  90. package/dist/compare.js +0 -354
  91. package/dist/compare.js.map +0 -1
  92. package/dist/context/index.d.ts +0 -41
  93. package/dist/context/index.d.ts.map +0 -1
  94. package/dist/context/index.js +0 -253
  95. package/dist/context/index.js.map +0 -1
  96. package/dist/gates/delta.d.ts +0 -45
  97. package/dist/gates/delta.d.ts.map +0 -1
  98. package/dist/gates/delta.js +0 -260
  99. package/dist/gates/delta.js.map +0 -1
  100. package/dist/gates/index.d.ts +0 -9
  101. package/dist/gates/index.d.ts.map +0 -1
  102. package/dist/gates/index.js +0 -9
  103. package/dist/gates/index.js.map +0 -1
  104. package/dist/gates/types.d.ts +0 -96
  105. package/dist/gates/types.d.ts.map +0 -1
  106. package/dist/gates/types.js +0 -59
  107. package/dist/gates/types.js.map +0 -1
  108. package/dist/graph/call.d.ts +0 -63
  109. package/dist/graph/call.d.ts.map +0 -1
  110. package/dist/graph/call.js +0 -240
  111. package/dist/graph/call.js.map +0 -1
  112. package/dist/graph/dependency.d.ts +0 -52
  113. package/dist/graph/dependency.d.ts.map +0 -1
  114. package/dist/graph/dependency.js +0 -296
  115. package/dist/graph/dependency.js.map +0 -1
  116. package/dist/graph/index.d.ts +0 -3
  117. package/dist/graph/index.d.ts.map +0 -1
  118. package/dist/graph/index.js +0 -3
  119. package/dist/graph/index.js.map +0 -1
  120. package/dist/metrics/cognitive.d.ts +0 -42
  121. package/dist/metrics/cognitive.d.ts.map +0 -1
  122. package/dist/metrics/cognitive.js +0 -204
  123. package/dist/metrics/cognitive.js.map +0 -1
  124. package/dist/metrics/cyclomatic.d.ts +0 -31
  125. package/dist/metrics/cyclomatic.d.ts.map +0 -1
  126. package/dist/metrics/cyclomatic.js +0 -121
  127. package/dist/metrics/cyclomatic.js.map +0 -1
  128. package/dist/metrics/dimensional.d.ts +0 -32
  129. package/dist/metrics/dimensional.d.ts.map +0 -1
  130. package/dist/metrics/dimensional.js +0 -560
  131. package/dist/metrics/dimensional.js.map +0 -1
  132. package/dist/metrics/index.d.ts +0 -26
  133. package/dist/metrics/index.d.ts.map +0 -1
  134. package/dist/metrics/index.js +0 -120
  135. package/dist/metrics/index.js.map +0 -1
  136. package/dist/metrics/meta.d.ts +0 -90
  137. package/dist/metrics/meta.d.ts.map +0 -1
  138. package/dist/metrics/meta.js +0 -144
  139. package/dist/metrics/meta.js.map +0 -1
  140. package/dist/tensor/canonical.d.ts +0 -53
  141. package/dist/tensor/canonical.d.ts.map +0 -1
  142. package/dist/tensor/canonical.js +0 -192
  143. package/dist/tensor/canonical.js.map +0 -1
  144. package/dist/tensor/index.d.ts +0 -22
  145. package/dist/tensor/index.d.ts.map +0 -1
  146. package/dist/tensor/index.js +0 -22
  147. package/dist/tensor/index.js.map +0 -1
  148. package/dist/tensor/matrix.d.ts +0 -62
  149. package/dist/tensor/matrix.d.ts.map +0 -1
  150. package/dist/tensor/matrix.js +0 -187
  151. package/dist/tensor/matrix.js.map +0 -1
  152. package/dist/tensor/scoring.d.ts +0 -82
  153. package/dist/tensor/scoring.d.ts.map +0 -1
  154. package/dist/tensor/scoring.js +0 -233
  155. package/dist/tensor/scoring.js.map +0 -1
  156. package/dist/tensor/types.d.ts +0 -186
  157. package/dist/tensor/types.d.ts.map +0 -1
  158. package/dist/tensor/types.js +0 -15
  159. package/dist/tensor/types.js.map +0 -1
  160. package/dist/types.d.ts +0 -341
  161. package/dist/types.d.ts.map +0 -1
  162. package/dist/types.js +0 -22
  163. package/dist/types.js.map +0 -1
@@ -0,0 +1,881 @@
1
+ /**
2
+ * 🧀 Cheese Analyzer - Cognitive Accessibility (TypeScript)
3
+ *
4
+ * 인지 가능 조건 (4가지 모두 충족):
5
+ * 1. 중첩 깊이 ≤ 4
6
+ * 2. 개념 수 ≤ 9/함수 (Miller's Law: 7±2)
7
+ * 3. 숨겨진 의존성 ≤ 2
8
+ * 4. state×async×retry 2개 이상 공존 금지
9
+ *
10
+ * TypeScript 전용 복잡도 분석:
11
+ * - 제네릭 복잡도 (타입 파라미터 수, constraints, nested)
12
+ * - 유니온/인터섹션 타입 복잡도
13
+ * - 조건부 타입 복잡도
14
+ * - 매핑 타입 복잡도
15
+ * - 타입 가드 복잡도
16
+ * - 데코레이터 스택 복잡도
17
+ *
18
+ * Anti-pattern Penalty (TypeScript):
19
+ * - Rest parameters (...args) → +3 개념
20
+ * - Object spread for config bundling → +3 개념
21
+ * - Complex generics (>3 params or nested) → +2 per level
22
+ * - Deep union types (>4 members) → +1 per extra
23
+ * - Conditional type chains → +2 per level
24
+ */
25
+ import ts from 'typescript';
26
+ // =============================================================
27
+ // Essential Complexity Waiver
28
+ // =============================================================
29
+ // @ts-ignore: Waiver declaration for gate system - parsed at runtime
30
+ const __essential_complexity__ = {
31
+ adr: '../../docs/adr/001-ast-analyzer-complexity.md',
32
+ nesting: 7,
33
+ concepts: { total: 26 },
34
+ reason: 'AST Visitor 패턴 - 본질적 복잡도',
35
+ };
36
+ const FRAMEWORK_CONFIGS = {
37
+ react: {
38
+ name: 'react',
39
+ jsxNestingWeight: 0.3,
40
+ hookConceptWeight: 0.5,
41
+ chainMethodWeight: 0.5,
42
+ propsDestructureWeight: 0.3,
43
+ },
44
+ vue: {
45
+ name: 'vue',
46
+ jsxNestingWeight: 0.3,
47
+ hookConceptWeight: 0.5, // Composition API
48
+ chainMethodWeight: 0.5,
49
+ propsDestructureWeight: 0.3,
50
+ },
51
+ angular: {
52
+ name: 'angular',
53
+ jsxNestingWeight: 0.4,
54
+ hookConceptWeight: 0.7,
55
+ chainMethodWeight: 0.5,
56
+ propsDestructureWeight: 0.5,
57
+ },
58
+ svelte: {
59
+ name: 'svelte',
60
+ jsxNestingWeight: 0.3,
61
+ hookConceptWeight: 0.5,
62
+ chainMethodWeight: 0.5,
63
+ propsDestructureWeight: 0.3,
64
+ },
65
+ none: {
66
+ name: 'none',
67
+ jsxNestingWeight: 1.0,
68
+ hookConceptWeight: 1.0,
69
+ chainMethodWeight: 1.0,
70
+ propsDestructureWeight: 1.0,
71
+ },
72
+ };
73
+ // Hook/Composition API 패턴
74
+ const HOOK_PATTERNS = {
75
+ react: [
76
+ /\buse[A-Z]\w*\s*\(/, // useState, useEffect, useCustomHook
77
+ /\buseState\b/,
78
+ /\buseEffect\b/,
79
+ /\buseCallback\b/,
80
+ /\buseMemo\b/,
81
+ /\buseRef\b/,
82
+ /\buseContext\b/,
83
+ /\buseReducer\b/,
84
+ ],
85
+ vue: [
86
+ /\bref\s*\(/,
87
+ /\breactive\s*\(/,
88
+ /\bcomputed\s*\(/,
89
+ /\bwatch\s*\(/,
90
+ /\bwatchEffect\s*\(/,
91
+ /\bonMounted\s*\(/,
92
+ /\bonUnmounted\s*\(/,
93
+ ],
94
+ angular: [
95
+ /\b@Input\s*\(/,
96
+ /\b@Output\s*\(/,
97
+ /\b@ViewChild\s*\(/,
98
+ /\bngOnInit\b/,
99
+ ],
100
+ svelte: [
101
+ /\$:\s*/, // reactive statements
102
+ /\bonMount\s*\(/,
103
+ /\bonDestroy\s*\(/,
104
+ ],
105
+ none: [],
106
+ };
107
+ // 체이닝 메서드 패턴
108
+ const CHAIN_METHOD_PATTERNS = [
109
+ /\.map\s*\(/,
110
+ /\.filter\s*\(/,
111
+ /\.reduce\s*\(/,
112
+ /\.find\s*\(/,
113
+ /\.findIndex\s*\(/,
114
+ /\.some\s*\(/,
115
+ /\.every\s*\(/,
116
+ /\.flatMap\s*\(/,
117
+ /\.forEach\s*\(/,
118
+ ];
119
+ // =============================================================
120
+ // Default Config
121
+ // =============================================================
122
+ const DEFAULT_CONFIG = {
123
+ nestingThreshold: 4,
124
+ conceptsPerFunction: 9, // Miller's Law: 7±2
125
+ hiddenDepThreshold: 2,
126
+ };
127
+ // Anti-pattern penalties
128
+ const REST_PARAMS_PENALTY = 3;
129
+ const SPREAD_CONFIG_PENALTY = 3;
130
+ // TypeScript 타입 시스템 페널티
131
+ const GENERIC_PARAMS_THRESHOLD = 3; // 3개 초과 시 페널티
132
+ const GENERIC_PARAMS_PENALTY = 2; // 초과당 +2
133
+ const GENERIC_DEPTH_THRESHOLD = 2; // 중첩 2 초과 시 페널티
134
+ const GENERIC_DEPTH_PENALTY = 2; // 레벨당 +2
135
+ const GENERIC_CONSTRAINT_PENALTY = 1; // constraint당 +1
136
+ const UNION_MEMBERS_THRESHOLD = 4; // 4개 초과 시 페널티
137
+ const UNION_MEMBERS_PENALTY = 1; // 초과당 +1
138
+ const INTERSECTION_PENALTY = 1; // 인터섹션당 +1
139
+ const CONDITIONAL_TYPE_PENALTY = 2; // 조건부 타입당 +2
140
+ const MAPPED_TYPE_PENALTY = 2; // 매핑 타입당 +2
141
+ const TYPE_GUARD_PENALTY = 1; // 타입 가드당 +1
142
+ const DECORATOR_STACK_THRESHOLD = 3; // 3개 이상 연속 시 페널티
143
+ const DECORATOR_STACK_PENALTY = 2; // 스택당 +2
144
+ // Built-in functions (low cognitive load)
145
+ const BUILTIN_FUNCTIONS = new Set([
146
+ 'parseInt', 'parseFloat', 'String', 'Number', 'Boolean',
147
+ 'Array', 'Object', 'JSON', 'Math', 'Date', 'Promise',
148
+ 'console', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
149
+ 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
150
+ ]);
151
+ // =============================================================
152
+ // Main Analyzer
153
+ // =============================================================
154
+ export function analyzeCheese(source, config = DEFAULT_CONFIG) {
155
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
156
+ // 프레임워크 감지 및 보정
157
+ const frameworkType = config.framework || detectFramework(source);
158
+ const frameworkConfig = FRAMEWORK_CONFIGS[frameworkType];
159
+ const frameworkInfo = analyzeFrameworkPatterns(sourceFile, source, frameworkConfig);
160
+ // 중첩 분석 (JSX와 로직 분리)
161
+ const { maxNesting, jsxNesting, logicNesting } = calculateNestingWithFramework(sourceFile);
162
+ frameworkInfo.jsxNestingDepth = jsxNesting;
163
+ frameworkInfo.logicNestingDepth = logicNesting;
164
+ // 보정된 중첩 계산
165
+ const adjustedNesting = calculateAdjustedNesting(jsxNesting, logicNesting, frameworkConfig, frameworkInfo.adjustments);
166
+ const functions = analyzeFunctionsWithFramework(sourceFile, source, frameworkConfig, frameworkInfo);
167
+ const hiddenDeps = countHiddenDependencies(sourceFile);
168
+ const stateAsyncRetry = checkStateAsyncRetry(source, sourceFile);
169
+ // TypeScript 전용 복잡도 분석
170
+ const typeComplexity = analyzeTypeComplexity(sourceFile);
171
+ const violations = collectViolations(adjustedNesting, // 보정된 값 사용
172
+ functions, hiddenDeps, stateAsyncRetry, typeComplexity, config);
173
+ return {
174
+ accessible: violations.length === 0,
175
+ reason: violations.length === 0 ? 'All conditions met' : violations[0],
176
+ violations,
177
+ maxNesting,
178
+ adjustedNesting,
179
+ functions,
180
+ hiddenDependencies: hiddenDeps,
181
+ stateAsyncRetry,
182
+ config,
183
+ typeComplexity,
184
+ frameworkInfo,
185
+ };
186
+ }
187
+ // =============================================================
188
+ // Helper Functions
189
+ // =============================================================
190
+ function getFunctionName(node) {
191
+ if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
192
+ return node.name?.getText() || '<anonymous>';
193
+ }
194
+ // Arrow function: check parent for variable name
195
+ const parent = node.parent;
196
+ if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
197
+ return parent.name.text;
198
+ }
199
+ if (ts.isPropertyAssignment(parent) && ts.isIdentifier(parent.name)) {
200
+ return parent.name.text;
201
+ }
202
+ return '<anonymous>';
203
+ }
204
+ function getCallName(node, sourceFile) {
205
+ const expr = node.expression;
206
+ if (ts.isIdentifier(expr)) {
207
+ return expr.text;
208
+ }
209
+ if (ts.isPropertyAccessExpression(expr)) {
210
+ return expr.getText(sourceFile);
211
+ }
212
+ return null;
213
+ }
214
+ // =============================================================
215
+ // Condition 3: Hidden Dependencies
216
+ // =============================================================
217
+ function countHiddenDependencies(sourceFile) {
218
+ let count = 0;
219
+ function visit(node) {
220
+ // Global access
221
+ if (ts.isIdentifier(node)) {
222
+ const name = node.text;
223
+ if (['global', 'globalThis', 'window', 'document'].includes(name)) {
224
+ count++;
225
+ }
226
+ }
227
+ // process.env access
228
+ if (ts.isPropertyAccessExpression(node)) {
229
+ const text = node.getText(sourceFile);
230
+ if (text.startsWith('process.env') || text.startsWith('import.meta.env')) {
231
+ count++;
232
+ }
233
+ }
234
+ ts.forEachChild(node, visit);
235
+ }
236
+ visit(sourceFile);
237
+ return count;
238
+ }
239
+ // =============================================================
240
+ // Condition 4: State × Async × Retry
241
+ // =============================================================
242
+ function checkStateAsyncRetry(source, sourceFile) {
243
+ // State detection
244
+ const hasState = detectState(source, sourceFile);
245
+ // Async detection
246
+ const hasAsync = detectAsync(source, sourceFile);
247
+ // Retry detection
248
+ const hasRetry = detectRetry(source);
249
+ const axes = [];
250
+ if (hasState)
251
+ axes.push('state');
252
+ if (hasAsync)
253
+ axes.push('async');
254
+ if (hasRetry)
255
+ axes.push('retry');
256
+ return {
257
+ hasState,
258
+ hasAsync,
259
+ hasRetry,
260
+ axes,
261
+ count: axes.length,
262
+ violated: axes.length >= 2,
263
+ };
264
+ }
265
+ function detectState(source, sourceFile) {
266
+ // Class field mutation (this.field = ...)
267
+ if (/this\.\w+\s*=/.test(source)) {
268
+ return true;
269
+ }
270
+ // React state
271
+ if (/useState|useReducer|setState/.test(source)) {
272
+ return true;
273
+ }
274
+ // Zustand/Redux/MobX
275
+ if (/createStore|makeAutoObservable|observable/.test(source)) {
276
+ return true;
277
+ }
278
+ // Let variable with reassignment
279
+ let hasLetReassignment = false;
280
+ function visit(node) {
281
+ if (ts.isVariableDeclaration(node)) {
282
+ const parent = node.parent;
283
+ if (ts.isVariableDeclarationList(parent) && parent.flags & ts.NodeFlags.Let) {
284
+ // Check if reassigned later (simple heuristic)
285
+ const varName = node.name.getText(sourceFile);
286
+ if (new RegExp(`${varName}\\s*=(?!=)`).test(source)) {
287
+ hasLetReassignment = true;
288
+ }
289
+ }
290
+ }
291
+ ts.forEachChild(node, visit);
292
+ }
293
+ visit(sourceFile);
294
+ return hasLetReassignment;
295
+ }
296
+ function detectAsync(source, _sourceFile) {
297
+ // Explicit async/await
298
+ if (/async\s+(function|\()|\bawait\b/.test(source)) {
299
+ return true;
300
+ }
301
+ // Promise usage
302
+ if (/new\s+Promise|\.then\s*\(|\.catch\s*\(/.test(source)) {
303
+ return true;
304
+ }
305
+ // Worker/concurrent
306
+ if (/Worker|spawn|fork/.test(source)) {
307
+ return true;
308
+ }
309
+ return false;
310
+ }
311
+ function detectRetry(source) {
312
+ // Retry decorators/libraries
313
+ const retryPatterns = [
314
+ /@retry/i,
315
+ /retry\s*\(/i,
316
+ /p-retry|async-retry/i,
317
+ /exponentialBackoff/i,
318
+ /retryable/i,
319
+ ];
320
+ if (retryPatterns.some(p => p.test(source))) {
321
+ return true;
322
+ }
323
+ // Manual retry pattern: for/while + try/catch + continue/break
324
+ if (/for\s*\([^)]*retry|while\s*\([^)]*retry/i.test(source)) {
325
+ return true;
326
+ }
327
+ // loop with try-catch and counter
328
+ if (/for\s*\(.*attempts?|while\s*\(.*attempts?/i.test(source)) {
329
+ return true;
330
+ }
331
+ return false;
332
+ }
333
+ // =============================================================
334
+ // Collect Violations
335
+ // =============================================================
336
+ function collectViolations(maxNesting, functions, hiddenDeps, sar, typeComplexity, config) {
337
+ const violations = [];
338
+ // 1. Nesting
339
+ if (maxNesting > config.nestingThreshold) {
340
+ violations.push(`nesting depth ${maxNesting} > ${config.nestingThreshold}`);
341
+ }
342
+ // 2. Concepts per function (타입 복잡도 페널티 포함)
343
+ const typePenaltyPerFunction = Math.floor(typeComplexity.totalPenalty / Math.max(functions.length, 1));
344
+ for (const fn of functions) {
345
+ const totalConcepts = fn.conceptCount + typePenaltyPerFunction;
346
+ if (totalConcepts > config.conceptsPerFunction) {
347
+ violations.push(`${fn.name}: ${totalConcepts} concepts > ${config.conceptsPerFunction} (type penalty: ${typePenaltyPerFunction})`);
348
+ }
349
+ }
350
+ // 3. Hidden dependencies
351
+ if (hiddenDeps > config.hiddenDepThreshold) {
352
+ violations.push(`hidden deps ${hiddenDeps} > ${config.hiddenDepThreshold}`);
353
+ }
354
+ // 4. State × Async × Retry
355
+ if (sar.violated) {
356
+ violations.push(`state×async×retry: ${sar.axes.join('×')} (${sar.count} >= 2)`);
357
+ }
358
+ // 5. TypeScript 타입 복잡도 위반
359
+ if (typeComplexity.generics.maxParams > GENERIC_PARAMS_THRESHOLD + 2) {
360
+ violations.push(`excessive generic params: ${typeComplexity.generics.maxParams} > ${GENERIC_PARAMS_THRESHOLD + 2}`);
361
+ }
362
+ if (typeComplexity.generics.maxDepth > GENERIC_DEPTH_THRESHOLD + 1) {
363
+ violations.push(`deeply nested generics: depth ${typeComplexity.generics.maxDepth}`);
364
+ }
365
+ if (typeComplexity.unions.maxMembers > UNION_MEMBERS_THRESHOLD + 4) {
366
+ violations.push(`excessive union members: ${typeComplexity.unions.maxMembers} > ${UNION_MEMBERS_THRESHOLD + 4}`);
367
+ }
368
+ if (typeComplexity.conditionalTypes > 3) {
369
+ violations.push(`too many conditional types: ${typeComplexity.conditionalTypes}`);
370
+ }
371
+ if (typeComplexity.decoratorStacks > 0) {
372
+ violations.push(`decorator stacks detected: ${typeComplexity.decoratorStacks}`);
373
+ }
374
+ return violations;
375
+ }
376
+ // =============================================================
377
+ // TypeScript Type Complexity Analysis
378
+ // =============================================================
379
+ function analyzeTypeComplexity(sourceFile) {
380
+ const generics = analyzeGenericComplexity(sourceFile);
381
+ const unions = analyzeUnionComplexity(sourceFile);
382
+ const conditionalTypes = countConditionalTypes(sourceFile);
383
+ const mappedTypes = countMappedTypes(sourceFile);
384
+ const typeGuards = countTypeGuards(sourceFile);
385
+ const decoratorStacks = countDecoratorStacks(sourceFile);
386
+ const totalPenalty = generics.penalty +
387
+ unions.penalty +
388
+ conditionalTypes * CONDITIONAL_TYPE_PENALTY +
389
+ mappedTypes * MAPPED_TYPE_PENALTY +
390
+ typeGuards * TYPE_GUARD_PENALTY +
391
+ decoratorStacks * DECORATOR_STACK_PENALTY;
392
+ return {
393
+ generics,
394
+ unions,
395
+ conditionalTypes,
396
+ mappedTypes,
397
+ typeGuards,
398
+ decoratorStacks,
399
+ totalPenalty,
400
+ };
401
+ }
402
+ /**
403
+ * 타입 파라미터 정보 추출 (개념 수 ~5)
404
+ */
405
+ function extractTypeParameterInfo(typeParameters, typeNode) {
406
+ if (!typeParameters)
407
+ return { paramCount: 0, constrainedCount: 0, depth: 0 };
408
+ const paramCount = typeParameters.length;
409
+ const constrainedCount = typeParameters.filter(p => p.constraint).length;
410
+ const depth = typeNode ? getGenericDepth(typeNode) : 0;
411
+ return { paramCount, constrainedCount, depth };
412
+ }
413
+ /**
414
+ * 제네릭 복잡도 분석 (개념 수 ~8)
415
+ */
416
+ function analyzeGenericComplexity(sourceFile) {
417
+ let count = 0;
418
+ let maxParams = 0;
419
+ let maxDepth = 0;
420
+ let constrainedCount = 0;
421
+ function processGenericNode(info) {
422
+ count++;
423
+ if (info.paramCount > maxParams)
424
+ maxParams = info.paramCount;
425
+ if (info.depth > maxDepth)
426
+ maxDepth = info.depth;
427
+ constrainedCount += info.constrainedCount;
428
+ }
429
+ function visit(node) {
430
+ if (ts.isTypeAliasDeclaration(node) && node.typeParameters) {
431
+ processGenericNode(extractTypeParameterInfo(node.typeParameters, node.type));
432
+ }
433
+ if (ts.isInterfaceDeclaration(node) && node.typeParameters) {
434
+ processGenericNode(extractTypeParameterInfo(node.typeParameters));
435
+ }
436
+ if ((ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node)) && node.typeParameters) {
437
+ processGenericNode(extractTypeParameterInfo(node.typeParameters));
438
+ }
439
+ if (ts.isClassDeclaration(node) && node.typeParameters) {
440
+ processGenericNode(extractTypeParameterInfo(node.typeParameters));
441
+ }
442
+ ts.forEachChild(node, visit);
443
+ }
444
+ visit(sourceFile);
445
+ const penalty = calculateGenericPenalty(maxParams, maxDepth, constrainedCount);
446
+ return { count, maxParams, maxDepth, constrainedCount, penalty };
447
+ }
448
+ /**
449
+ * 제네릭 페널티 계산 (개념 수 ~4)
450
+ */
451
+ function calculateGenericPenalty(maxParams, maxDepth, constrainedCount) {
452
+ let penalty = 0;
453
+ if (maxParams > GENERIC_PARAMS_THRESHOLD) {
454
+ penalty += (maxParams - GENERIC_PARAMS_THRESHOLD) * GENERIC_PARAMS_PENALTY;
455
+ }
456
+ if (maxDepth > GENERIC_DEPTH_THRESHOLD) {
457
+ penalty += (maxDepth - GENERIC_DEPTH_THRESHOLD) * GENERIC_DEPTH_PENALTY;
458
+ }
459
+ penalty += constrainedCount * GENERIC_CONSTRAINT_PENALTY;
460
+ return penalty;
461
+ }
462
+ function getGenericDepth(node) {
463
+ let maxDepth = 0;
464
+ function visit(n, depth) {
465
+ if (ts.isTypeReferenceNode(n) && n.typeArguments) {
466
+ depth++;
467
+ if (depth > maxDepth)
468
+ maxDepth = depth;
469
+ n.typeArguments.forEach(arg => visit(arg, depth));
470
+ }
471
+ else {
472
+ ts.forEachChild(n, child => visit(child, depth));
473
+ }
474
+ }
475
+ visit(node, 0);
476
+ return maxDepth;
477
+ }
478
+ function analyzeUnionComplexity(sourceFile) {
479
+ let count = 0;
480
+ let maxMembers = 0;
481
+ let intersectionCount = 0;
482
+ function visit(node) {
483
+ // Union type: A | B | C
484
+ if (ts.isUnionTypeNode(node)) {
485
+ count++;
486
+ const members = node.types.length;
487
+ if (members > maxMembers)
488
+ maxMembers = members;
489
+ }
490
+ // Intersection type: A & B & C
491
+ if (ts.isIntersectionTypeNode(node)) {
492
+ intersectionCount++;
493
+ }
494
+ ts.forEachChild(node, visit);
495
+ }
496
+ visit(sourceFile);
497
+ // Calculate penalty
498
+ let penalty = 0;
499
+ if (maxMembers > UNION_MEMBERS_THRESHOLD) {
500
+ penalty += (maxMembers - UNION_MEMBERS_THRESHOLD) * UNION_MEMBERS_PENALTY;
501
+ }
502
+ penalty += intersectionCount * INTERSECTION_PENALTY;
503
+ return { count, maxMembers, intersectionCount, penalty };
504
+ }
505
+ function countConditionalTypes(sourceFile) {
506
+ let count = 0;
507
+ function visit(node) {
508
+ // Conditional type: T extends U ? X : Y
509
+ if (ts.isConditionalTypeNode(node)) {
510
+ count++;
511
+ }
512
+ ts.forEachChild(node, visit);
513
+ }
514
+ visit(sourceFile);
515
+ return count;
516
+ }
517
+ function countMappedTypes(sourceFile) {
518
+ let count = 0;
519
+ function visit(node) {
520
+ // Mapped type: { [K in keyof T]: ... }
521
+ if (ts.isMappedTypeNode(node)) {
522
+ count++;
523
+ }
524
+ ts.forEachChild(node, visit);
525
+ }
526
+ visit(sourceFile);
527
+ return count;
528
+ }
529
+ function countTypeGuards(sourceFile) {
530
+ let count = 0;
531
+ function visit(node) {
532
+ // Type predicate: x is Foo
533
+ if (ts.isTypePredicateNode(node)) {
534
+ count++;
535
+ }
536
+ // Function with type predicate return type
537
+ if ((ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node)) &&
538
+ node.type &&
539
+ ts.isTypePredicateNode(node.type)) {
540
+ count++;
541
+ }
542
+ ts.forEachChild(node, visit);
543
+ }
544
+ visit(sourceFile);
545
+ return count;
546
+ }
547
+ function countDecoratorStacks(sourceFile) {
548
+ let stacks = 0;
549
+ function visit(node) {
550
+ // Check for decorator stacks on classes, methods, properties
551
+ const decorators = ts.canHaveDecorators(node) ? ts.getDecorators(node) : undefined;
552
+ if (decorators && decorators.length >= DECORATOR_STACK_THRESHOLD) {
553
+ stacks++;
554
+ }
555
+ ts.forEachChild(node, visit);
556
+ }
557
+ visit(sourceFile);
558
+ return stacks;
559
+ }
560
+ // =============================================================
561
+ // Framework Detection & Adjustment
562
+ // =============================================================
563
+ function detectFramework(source) {
564
+ // React 감지
565
+ if (/from\s+['"]react['"]/.test(source) ||
566
+ /import\s+React/.test(source) ||
567
+ /['"]react['"]/.test(source)) {
568
+ return 'react';
569
+ }
570
+ // Vue 감지
571
+ if (/from\s+['"]vue['"]/.test(source) ||
572
+ /defineComponent|ref\(|reactive\(/.test(source)) {
573
+ return 'vue';
574
+ }
575
+ // Angular 감지
576
+ if (/@Component\s*\(|@Injectable\s*\(|@NgModule/.test(source) ||
577
+ /from\s+['"]@angular/.test(source)) {
578
+ return 'angular';
579
+ }
580
+ // Svelte 감지
581
+ if (/from\s+['"]svelte['"]/.test(source) ||
582
+ /\$:\s*/.test(source)) {
583
+ return 'svelte';
584
+ }
585
+ return 'none';
586
+ }
587
+ function analyzeFrameworkPatterns(_sourceFile, source, config) {
588
+ const hookPatterns = HOOK_PATTERNS[config.name];
589
+ let hookCount = 0;
590
+ let chainCount = 0;
591
+ // Hook 패턴 카운트
592
+ for (const pattern of hookPatterns) {
593
+ const matches = source.match(new RegExp(pattern.source, 'g'));
594
+ if (matches) {
595
+ hookCount += matches.length;
596
+ }
597
+ }
598
+ // 체이닝 메서드 카운트
599
+ for (const pattern of CHAIN_METHOD_PATTERNS) {
600
+ const matches = source.match(new RegExp(pattern.source, 'g'));
601
+ if (matches) {
602
+ chainCount += matches.length;
603
+ }
604
+ }
605
+ return {
606
+ detected: config.name,
607
+ config,
608
+ jsxNestingDepth: 0, // 나중에 계산
609
+ logicNestingDepth: 0, // 나중에 계산
610
+ hookCount,
611
+ chainCount,
612
+ adjustments: [],
613
+ };
614
+ }
615
+ function calculateNestingWithFramework(sourceFile) {
616
+ let maxNesting = 0;
617
+ let maxJsxNesting = 0;
618
+ let maxLogicNesting = 0;
619
+ // JSX 요소 노드 종류
620
+ const jsxNodeKinds = new Set([
621
+ ts.SyntaxKind.JsxElement,
622
+ ts.SyntaxKind.JsxSelfClosingElement,
623
+ ts.SyntaxKind.JsxFragment,
624
+ ts.SyntaxKind.JsxOpeningElement,
625
+ ]);
626
+ // 로직 중첩 노드 종류
627
+ const logicNodeKinds = new Set([
628
+ ts.SyntaxKind.IfStatement,
629
+ ts.SyntaxKind.ForStatement,
630
+ ts.SyntaxKind.ForInStatement,
631
+ ts.SyntaxKind.ForOfStatement,
632
+ ts.SyntaxKind.WhileStatement,
633
+ ts.SyntaxKind.DoStatement,
634
+ ts.SyntaxKind.TryStatement,
635
+ ts.SyntaxKind.CatchClause,
636
+ ts.SyntaxKind.SwitchStatement,
637
+ ts.SyntaxKind.ConditionalExpression,
638
+ ]);
639
+ function visit(node, totalDepth, jsxDepth, logicDepth) {
640
+ let newTotalDepth = totalDepth;
641
+ let newJsxDepth = jsxDepth;
642
+ let newLogicDepth = logicDepth;
643
+ if (jsxNodeKinds.has(node.kind)) {
644
+ newTotalDepth++;
645
+ newJsxDepth++;
646
+ if (newJsxDepth > maxJsxNesting)
647
+ maxJsxNesting = newJsxDepth;
648
+ }
649
+ else if (logicNodeKinds.has(node.kind)) {
650
+ newTotalDepth++;
651
+ newLogicDepth++;
652
+ if (newLogicDepth > maxLogicNesting)
653
+ maxLogicNesting = newLogicDepth;
654
+ }
655
+ else if (node.kind === ts.SyntaxKind.ArrowFunction ||
656
+ node.kind === ts.SyntaxKind.FunctionExpression ||
657
+ node.kind === ts.SyntaxKind.FunctionDeclaration ||
658
+ node.kind === ts.SyntaxKind.MethodDeclaration) {
659
+ newTotalDepth++;
660
+ newLogicDepth++;
661
+ if (newLogicDepth > maxLogicNesting)
662
+ maxLogicNesting = newLogicDepth;
663
+ }
664
+ if (newTotalDepth > maxNesting)
665
+ maxNesting = newTotalDepth;
666
+ ts.forEachChild(node, child => visit(child, newTotalDepth, newJsxDepth, newLogicDepth));
667
+ }
668
+ visit(sourceFile, 0, 0, 0);
669
+ return {
670
+ maxNesting,
671
+ jsxNesting: maxJsxNesting,
672
+ logicNesting: maxLogicNesting,
673
+ };
674
+ }
675
+ function calculateAdjustedNesting(jsxNesting, logicNesting, config, adjustments) {
676
+ // JSX 중첩에 가중치 적용
677
+ const adjustedJsx = Math.ceil(jsxNesting * config.jsxNestingWeight);
678
+ if (jsxNesting > 0 && config.jsxNestingWeight < 1) {
679
+ adjustments.push({
680
+ type: 'jsx_nesting',
681
+ original: jsxNesting,
682
+ adjusted: adjustedJsx,
683
+ reason: `JSX nesting weighted by ${config.jsxNestingWeight}`,
684
+ });
685
+ }
686
+ // 최종 중첩 = 로직 중첩 + 보정된 JSX 중첩
687
+ return logicNesting + adjustedJsx;
688
+ }
689
+ function analyzeFunctionsWithFramework(sourceFile, source, frameworkConfig, frameworkInfo) {
690
+ const functions = [];
691
+ function visit(node) {
692
+ if (ts.isFunctionDeclaration(node) ||
693
+ ts.isMethodDeclaration(node) ||
694
+ ts.isArrowFunction(node) ||
695
+ ts.isFunctionExpression(node)) {
696
+ const info = analyzeFunctionWithFramework(node, sourceFile, source, frameworkConfig, frameworkInfo);
697
+ if (info) {
698
+ functions.push(info);
699
+ }
700
+ }
701
+ ts.forEachChild(node, visit);
702
+ }
703
+ visit(sourceFile);
704
+ return functions;
705
+ }
706
+ /**
707
+ * 파라미터 분석 (개념 수 ~7)
708
+ */
709
+ function analyzeParameters(node, sourceFile, frameworkConfig, frameworkInfo) {
710
+ const concepts = [];
711
+ const antiPatterns = [];
712
+ if (!node.parameters)
713
+ return { concepts, antiPatterns };
714
+ for (const param of node.parameters) {
715
+ const paramName = param.name.getText(sourceFile);
716
+ if (paramName === 'this')
717
+ continue;
718
+ if (ts.isObjectBindingPattern(param.name)) {
719
+ const bindingCount = param.name.elements.length;
720
+ const adjustedCount = Math.ceil(bindingCount * frameworkConfig.propsDestructureWeight);
721
+ for (let i = 0; i < adjustedCount; i++) {
722
+ concepts.push(`prop:destructured_${i}`);
723
+ }
724
+ if (frameworkConfig.propsDestructureWeight < 1) {
725
+ frameworkInfo.adjustments.push({
726
+ type: 'props',
727
+ original: bindingCount,
728
+ adjusted: adjustedCount,
729
+ reason: `Props destructuring weighted by ${frameworkConfig.propsDestructureWeight}`,
730
+ });
731
+ }
732
+ }
733
+ else {
734
+ concepts.push(`param:${paramName}`);
735
+ }
736
+ if (param.dotDotDotToken) {
737
+ antiPatterns.push({
738
+ pattern: 'rest_params',
739
+ penalty: REST_PARAMS_PENALTY,
740
+ reason: `...${paramName} hides actual parameter count`,
741
+ });
742
+ }
743
+ }
744
+ return { concepts, antiPatterns };
745
+ }
746
+ /**
747
+ * 변수 및 Hook 분석 (개념 수 ~8)
748
+ */
749
+ function analyzeVariablesWithHooks(node, sourceFile, hookPatterns) {
750
+ const variables = new Set();
751
+ let hookCount = 0;
752
+ function visit(n) {
753
+ if (ts.isVariableDeclaration(n) && ts.isIdentifier(n.name)) {
754
+ const varText = n.getText(sourceFile);
755
+ const isHook = hookPatterns.some(pattern => pattern.test(varText));
756
+ if (isHook) {
757
+ hookCount++;
758
+ }
759
+ else {
760
+ variables.add(n.name.text);
761
+ }
762
+ }
763
+ ts.forEachChild(n, visit);
764
+ }
765
+ if (node.body)
766
+ visit(node.body);
767
+ const concepts = Array.from(variables).map(v => `var:${v}`);
768
+ return { concepts, hookCount };
769
+ }
770
+ /**
771
+ * 함수 호출 및 Chain 메서드 분석 (개념 수 ~8)
772
+ */
773
+ function analyzeCallsWithChains(node, sourceFile) {
774
+ const calls = new Set();
775
+ let chainCount = 0;
776
+ function visit(n) {
777
+ if (ts.isCallExpression(n)) {
778
+ const callName = getCallName(n, sourceFile);
779
+ if (callName && !BUILTIN_FUNCTIONS.has(callName.split('.')[0])) {
780
+ const isChain = CHAIN_METHOD_PATTERNS.some(pattern => pattern.test(callName));
781
+ if (isChain) {
782
+ chainCount++;
783
+ }
784
+ else {
785
+ calls.add(callName);
786
+ }
787
+ }
788
+ }
789
+ ts.forEachChild(n, visit);
790
+ }
791
+ if (node.body)
792
+ visit(node.body);
793
+ const concepts = Array.from(calls).map(c => `call:${c}`);
794
+ return { concepts, chainCount };
795
+ }
796
+ /**
797
+ * Spread config 안티패턴 분석 (개념 수 ~5)
798
+ */
799
+ function analyzeSpreadPatterns(node, sourceFile) {
800
+ const antiPatterns = [];
801
+ function visit(n) {
802
+ if (ts.isSpreadElement(n) || ts.isSpreadAssignment(n)) {
803
+ const text = n.getText(sourceFile);
804
+ if (/\.\.\.(options|config|props|params|args)/i.test(text)) {
805
+ antiPatterns.push({
806
+ pattern: 'spread_config',
807
+ penalty: SPREAD_CONFIG_PENALTY,
808
+ reason: `${text} bundles multiple concepts`,
809
+ });
810
+ }
811
+ }
812
+ ts.forEachChild(n, visit);
813
+ }
814
+ if (node.body)
815
+ visit(node.body);
816
+ return antiPatterns;
817
+ }
818
+ /**
819
+ * Hook/Chain 개념에 가중치 적용 (개념 수 ~6)
820
+ */
821
+ function applyFrameworkWeights(hookCount, chainCount, frameworkConfig, frameworkInfo) {
822
+ const concepts = [];
823
+ const adjustedHookCount = Math.ceil(hookCount * frameworkConfig.hookConceptWeight);
824
+ for (let i = 0; i < adjustedHookCount; i++) {
825
+ concepts.push(`hook:${i}`);
826
+ }
827
+ if (hookCount > 0 && frameworkConfig.hookConceptWeight < 1) {
828
+ frameworkInfo.adjustments.push({
829
+ type: 'hook',
830
+ original: hookCount,
831
+ adjusted: adjustedHookCount,
832
+ reason: `Hook concepts weighted by ${frameworkConfig.hookConceptWeight}`,
833
+ });
834
+ }
835
+ const adjustedChainCount = Math.ceil(chainCount * frameworkConfig.chainMethodWeight);
836
+ for (let i = 0; i < adjustedChainCount; i++) {
837
+ concepts.push(`chain:${i}`);
838
+ }
839
+ if (chainCount > 0 && frameworkConfig.chainMethodWeight < 1) {
840
+ frameworkInfo.adjustments.push({
841
+ type: 'chain',
842
+ original: chainCount,
843
+ adjusted: adjustedChainCount,
844
+ reason: `Chain methods weighted by ${frameworkConfig.chainMethodWeight}`,
845
+ });
846
+ }
847
+ return concepts;
848
+ }
849
+ /**
850
+ * 함수 분석 메인 (개념 수 ~8) - 조합만 수행
851
+ */
852
+ function analyzeFunctionWithFramework(node, sourceFile, _source, frameworkConfig, frameworkInfo) {
853
+ const name = getFunctionName(node);
854
+ const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
855
+ const hookPatterns = HOOK_PATTERNS[frameworkConfig.name];
856
+ // 분리된 분석 함수 호출
857
+ const paramResult = analyzeParameters(node, sourceFile, frameworkConfig, frameworkInfo);
858
+ const varResult = analyzeVariablesWithHooks(node, sourceFile, hookPatterns);
859
+ const callResult = analyzeCallsWithChains(node, sourceFile);
860
+ const spreadPatterns = analyzeSpreadPatterns(node, sourceFile);
861
+ const weightedConcepts = applyFrameworkWeights(varResult.hookCount, callResult.chainCount, frameworkConfig, frameworkInfo);
862
+ // 결과 조합
863
+ const concepts = [
864
+ ...paramResult.concepts,
865
+ ...varResult.concepts,
866
+ ...callResult.concepts,
867
+ ...weightedConcepts,
868
+ ];
869
+ const antiPatterns = [...paramResult.antiPatterns, ...spreadPatterns];
870
+ const rawConceptCount = concepts.length;
871
+ const penaltyTotal = antiPatterns.reduce((sum, ap) => sum + ap.penalty, 0);
872
+ return {
873
+ name,
874
+ line,
875
+ conceptCount: rawConceptCount + penaltyTotal,
876
+ rawConceptCount,
877
+ concepts,
878
+ antiPatterns,
879
+ };
880
+ }
881
+ //# sourceMappingURL=cheese.js.map