@archora/core 1.3.0 → 2.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.
@@ -1,4 +1,5 @@
1
1
  import type { ModuleId, ModuleNode, Recommendation, ScanResult } from '../analyzer/types';
2
+ import { buildDeadCodeReport, isReviewModule, unreachableRepair } from './buildDeadCodeReport';
2
3
 
3
4
  export interface FixPlanFinding {
4
5
  type:
@@ -223,33 +224,19 @@ function buildPriorityFindings(scan: ScanResult, generated: Set<ModuleId>): FixP
223
224
  });
224
225
  }
225
226
 
226
- for (const module of scan.modules) {
227
- if (!isReviewModule(module.id)) continue;
228
- const metrics = scan.metrics[module.id];
229
- const fanIn = metrics?.fanIn ?? 0;
230
- const fanOut = metrics?.fanOut ?? 0;
231
- if (
232
- fanIn === 0 &&
233
- fanOut === 0 &&
234
- module.exports.length === 0 &&
235
- module.kind !== 'entry' &&
236
- module.kind !== 'test' &&
237
- !module.isGenerated &&
238
- !module.isInfra
239
- ) {
240
- const repair = unreachableRepair(module.id);
241
- findings.push({
242
- type: 'unreachable-from-entries',
243
- id: `${module.id}:unreachable`,
244
- title: 'Unreachable module candidate',
245
- weight: Math.min(70, 35 + module.loc / 10),
246
- targets: [module.id],
247
- reason: 'No resolved imports connect this module to the analyzed dependency model.',
248
- action: repair.action,
249
- verify: repair.verify,
250
- params: { loc: module.loc, ...repair.params },
251
- });
252
- }
227
+ for (const candidate of buildDeadCodeReport(scan).candidates) {
228
+ const repair = unreachableRepair(candidate.id);
229
+ findings.push({
230
+ type: 'unreachable-from-entries',
231
+ id: `${candidate.id}:unreachable`,
232
+ title: 'Unreachable module candidate',
233
+ weight: Math.min(70, 35 + candidate.loc / 10),
234
+ targets: [candidate.id],
235
+ reason: candidate.reason,
236
+ action: repair.action,
237
+ verify: repair.verify,
238
+ params: { loc: candidate.loc, ...repair.params },
239
+ });
253
240
  }
254
241
 
255
242
  for (const violation of scan.contractViolations.slice(0, 50)) {
@@ -318,52 +305,10 @@ function buildPriorityFindings(scan: ScanResult, generated: Set<ModuleId>): FixP
318
305
  .slice(0, 100);
319
306
  }
320
307
 
321
- function isReviewModule(id: ModuleId): boolean {
322
- return !/(^|\/)(fixtures|test\/fixtures|__fixtures__|__tests__|__mocks__)(\/|$)|\.(test|spec)\./u.test(
323
- id,
324
- );
325
- }
326
-
327
308
  function isLikelyGeneratedPath(id: ModuleId): boolean {
328
309
  return /(^|\/)(generated|__generated__|openapi|swagger|graphql-codegen)(\/|$)/iu.test(id);
329
310
  }
330
311
 
331
- function unreachableRepair(id: ModuleId): {
332
- action: string;
333
- verify: string;
334
- params: Record<string, unknown>;
335
- } {
336
- if (isScriptEntryCandidate(id)) {
337
- return {
338
- action: `Treat ${id} as a script entry: add it to architecture entry configuration or exclude it from review scope; delete only after confirming no package script or CI job calls it.`,
339
- verify:
340
- 'Run archora report . --format fix-plan after entry/exclude config and confirm this script is no longer a priority finding.',
341
- params: { entryCandidate: 'script' },
342
- };
343
- }
344
-
345
- return {
346
- action:
347
- 'Check whether this file is dead code, dynamically loaded outside analyzer reach, or should be declared as an entry point.',
348
- verify:
349
- 'Re-scan after deletion, ignore, or entry-point configuration and confirm the candidate is gone.',
350
- params: {},
351
- };
352
- }
353
-
354
- function isScriptEntryCandidate(id: ModuleId): boolean {
355
- const normalized = id.replace(/\\/gu, '/');
356
- let scriptsIndex = 0;
357
- if (!normalized.startsWith('scripts/')) {
358
- const nestedIndex = normalized.indexOf('/scripts/');
359
- if (nestedIndex < 0) return false;
360
- scriptsIndex = nestedIndex + 1;
361
- }
362
- const file = normalized.slice(scriptsIndex + 'scripts/'.length);
363
- if (!file) return false;
364
- return file.endsWith('.js') || file.endsWith('.cjs') || file.endsWith('.mjs');
365
- }
366
-
367
312
  function barrelCycleRepair(
368
313
  scan: ScanResult,
369
314
  modules: readonly ModuleId[],
@@ -168,12 +168,18 @@ describe('analyzer view helpers', () => {
168
168
  changedModules: [],
169
169
  newCycles: current.cycles,
170
170
  resolvedCycles: [],
171
+ newLayerViolations: [],
172
+ resolvedLayerViolations: [],
173
+ newContractViolations: [],
174
+ resolvedContractViolations: [],
171
175
  summary: {
172
176
  addedModules: 0,
173
177
  removedModules: 0,
174
178
  changedModules: 0,
175
179
  newCycles: current.cycles.length,
176
180
  resolvedCycles: 0,
181
+ newLayerViolations: 0,
182
+ newContractViolations: 0,
177
183
  },
178
184
  },
179
185
  });
@@ -10,6 +10,7 @@ import type {
10
10
  import { detectLayer } from '../analyzer/layers';
11
11
  import { diffScans } from '../diff';
12
12
  import type { ScanDiff } from '../diff/types';
13
+ import { isReviewModule } from '../report/buildDeadCodeReport';
13
14
 
14
15
  export type MatrixGrouping = 'area' | 'layer' | 'folder' | 'package';
15
16
 
@@ -1188,12 +1189,6 @@ function isSourceFile(part: string): boolean {
1188
1189
  return /\.[cm]?[jt]sx?$/u.test(part) || /\.(vue|svelte)$/u.test(part);
1189
1190
  }
1190
1191
 
1191
- function isReviewModule(id: ModuleId): boolean {
1192
- return !/(^|\/)(fixtures|test\/fixtures|__fixtures__|__tests__|__mocks__)(\/|$)|\.(test|spec)\./u.test(
1193
- id,
1194
- );
1195
- }
1196
-
1197
1192
  function stripExtension(part: string): string {
1198
1193
  return part.replace(/\.[^.]+$/u, '');
1199
1194
  }