architecture-linter 0.1.4 → 0.1.6
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 +174 -0
- package/dist/baseline.d.ts +29 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +54 -0
- package/dist/baseline.js.map +1 -0
- package/dist/circularDetector.d.ts +16 -0
- package/dist/circularDetector.d.ts.map +1 -0
- package/dist/circularDetector.js +78 -0
- package/dist/circularDetector.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +275 -21
- package/dist/cli.js.map +1 -1
- package/dist/monorepo.d.ts +18 -0
- package/dist/monorepo.d.ts.map +1 -0
- package/dist/monorepo.js +61 -0
- package/dist/monorepo.js.map +1 -0
- package/dist/ruleEngine.js +1 -1
- package/dist/ruleEngine.js.map +1 -1
- package/dist/sarifFormatter.d.ts +51 -0
- package/dist/sarifFormatter.d.ts.map +1 -0
- package/dist/sarifFormatter.js +67 -0
- package/dist/sarifFormatter.js.map +1 -0
- package/dist/scorer.d.ts +11 -0
- package/dist/scorer.d.ts.map +1 -0
- package/dist/scorer.js +62 -0
- package/dist/scorer.js.map +1 -0
- package/dist/types.d.ts +47 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -309,6 +309,180 @@ architecture-linter ci
|
|
|
309
309
|
|
|
310
310
|
Fails safely if the workflow file already exists.
|
|
311
311
|
|
|
312
|
+
### `score`
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
architecture-linter score [options]
|
|
316
|
+
|
|
317
|
+
Options:
|
|
318
|
+
-c, --context <path> Path to the .context.yml config file (auto-detected)
|
|
319
|
+
-p, --project <path> Root directory of the project to scan (default: .)
|
|
320
|
+
-f, --format <format> Output format: text or json (default: text)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Calculates an **architecture health score from 0 to 100** based on three weighted components:
|
|
324
|
+
|
|
325
|
+
| Component | Max pts | What it measures |
|
|
326
|
+
|---|---|---|
|
|
327
|
+
| Violation density | 60 | How few import violations exist relative to total imports |
|
|
328
|
+
| Layer coverage | 25 | What fraction of files belong to a declared layer |
|
|
329
|
+
| Rule completeness | 15 | What fraction of layers have at least one rule defined |
|
|
330
|
+
|
|
331
|
+
**Grades:** A (90–100) · B (75–89) · C (60–74) · D (40–59) · F (0–39)
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
npx architecture-linter score
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Example output:
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
Architecture Health Score
|
|
341
|
+
|
|
342
|
+
87/100 Grade: B █████████████████░░░
|
|
343
|
+
|
|
344
|
+
Breakdown:
|
|
345
|
+
Violation density 52/60 pts
|
|
346
|
+
Layer coverage 25/25 pts
|
|
347
|
+
Rule completeness 10/15 pts
|
|
348
|
+
|
|
349
|
+
Stats:
|
|
350
|
+
Files scanned: 24
|
|
351
|
+
Total imports: 87
|
|
352
|
+
Violations: 4
|
|
353
|
+
Classified files: 24/24
|
|
354
|
+
Layers with rules: 2/3
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The score is also included in `scan --format json` output under the `score` key.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
### `badge`
|
|
362
|
+
|
|
363
|
+
Generates a [shields.io](https://shields.io) badge URL for the architecture health score. Drop it in your README to show the current grade at a glance.
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
architecture-linter badge [options]
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
| Option | Description | Default |
|
|
370
|
+
|---|---|---|
|
|
371
|
+
| `-c, --context <path>` | Path to `.context.yml` | auto-detect |
|
|
372
|
+
| `-p, --project <path>` | Project root directory | `.` |
|
|
373
|
+
| `-f, --format <format>` | Output format: `url` or `markdown` | `url` |
|
|
374
|
+
| `-o, --output <path>` | Write badge to a file instead of stdout | — |
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# Print a shields.io URL
|
|
378
|
+
npx architecture-linter badge
|
|
379
|
+
|
|
380
|
+
# Print a Markdown image tag ready to paste into your README
|
|
381
|
+
npx architecture-linter badge --format markdown
|
|
382
|
+
|
|
383
|
+
# Write the badge URL to a file (useful in CI)
|
|
384
|
+
npx architecture-linter badge --output .badge-url.txt
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Badge colours: **A** = bright green, **B** = green, **C** = yellow, **D** = orange, **F** = red.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
### `scan --format sarif`
|
|
392
|
+
|
|
393
|
+
Outputs a [SARIF 2.1.0](https://sarifweb.azurewebsites.net/) document — the standard format for GitHub Code Scanning. Upload it with the `github/codeql-action/upload-sarif` action to get violations as native PR annotations.
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
npx architecture-linter scan --format sarif > results.sarif
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Example GitHub Actions step:
|
|
400
|
+
|
|
401
|
+
```yaml
|
|
402
|
+
- name: Run architecture linter (SARIF)
|
|
403
|
+
run: npx architecture-linter scan --format sarif > arch.sarif
|
|
404
|
+
|
|
405
|
+
- name: Upload SARIF to GitHub Code Scanning
|
|
406
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
407
|
+
with:
|
|
408
|
+
sarif_file: arch.sarif
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
### `scan --baseline` — ratchet mode
|
|
414
|
+
|
|
415
|
+
Saves the current violation count and fails only when violations **increase**. Perfect for adopting the linter on an existing codebase without having to fix everything at once.
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
# First run: creates .arch-baseline.json and exits 0
|
|
419
|
+
npx architecture-linter scan --baseline
|
|
420
|
+
|
|
421
|
+
# Subsequent runs: fails only if violations exceeded the saved baseline
|
|
422
|
+
npx architecture-linter scan --baseline
|
|
423
|
+
|
|
424
|
+
# Explicitly update the baseline after cleaning up violations
|
|
425
|
+
npx architecture-linter scan --baseline --update-baseline
|
|
426
|
+
|
|
427
|
+
# Use a custom baseline file path
|
|
428
|
+
npx architecture-linter scan --baseline baselines/prod.json
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
The baseline file (default `.arch-baseline.json`) records the violation count, timestamp, and tool version. Commit it to track regression over time.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
### `scan --detect-circular`
|
|
436
|
+
|
|
437
|
+
Detects **circular dependencies between architectural layers** using Tarjan's SCC algorithm. A cycle exists when layer A (transitively) imports layer B which imports back into layer A.
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
npx architecture-linter scan --detect-circular
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Text output example:
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
↻ Circular dependencies detected between layers:
|
|
447
|
+
|
|
448
|
+
controller → service → controller
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Circular dependencies are also included in `--format json` output under the `circularDeps` key:
|
|
452
|
+
|
|
453
|
+
```json
|
|
454
|
+
{
|
|
455
|
+
"circularDeps": [
|
|
456
|
+
{ "cycle": ["controller", "service", "controller"] }
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
### `scan --monorepo`
|
|
464
|
+
|
|
465
|
+
Scans **all workspace packages** defined in the root `package.json` `workspaces` field. Each package uses its own `.context.yml` if one is present; otherwise falls back to the root config.
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
npx architecture-linter scan --monorepo
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Each package is scanned independently and prefixed in the output:
|
|
472
|
+
|
|
473
|
+
```
|
|
474
|
+
Scanning @myorg/api-gateway...
|
|
475
|
+
✅ No architecture violations found. (12 file(s) scanned)
|
|
476
|
+
|
|
477
|
+
Scanning @myorg/user-service...
|
|
478
|
+
❌ Architecture violation detected
|
|
479
|
+
...
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
The command exits `1` if any package has violations.
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
312
486
|
### Exit codes
|
|
313
487
|
|
|
314
488
|
| Code | Meaning |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface BaselineData {
|
|
2
|
+
/** Number of violations recorded when the baseline was saved. */
|
|
3
|
+
violationCount: number;
|
|
4
|
+
/** ISO timestamp of when the baseline was last written. */
|
|
5
|
+
timestamp: string;
|
|
6
|
+
/** Tool version that produced this baseline. */
|
|
7
|
+
version: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the baseline file path.
|
|
11
|
+
* `basePath` may be absolute or project-relative; defaults to `.arch-baseline.json`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveBaselinePath(basePath: string | undefined, projectDir: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Reads and parses an existing baseline file.
|
|
16
|
+
* Returns `null` if the file does not exist or cannot be parsed.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadBaseline(baselinePath: string): BaselineData | null;
|
|
19
|
+
/**
|
|
20
|
+
* Writes a baseline file with the current violation count.
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveBaseline(baselinePath: string, violationCount: number, toolVersion: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Returns `true` when the ratchet should fail — i.e. the current violation
|
|
25
|
+
* count has INCREASED beyond the saved baseline.
|
|
26
|
+
* Returns `false` when violations stayed the same or decreased.
|
|
27
|
+
*/
|
|
28
|
+
export declare function hasRatchetFailed(currentCount: number, baseline: BaselineData): boolean;
|
|
29
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG5F;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAOtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,IAAI,CAON;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAEtF"}
|
package/dist/baseline.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveBaselinePath = resolveBaselinePath;
|
|
7
|
+
exports.loadBaseline = loadBaseline;
|
|
8
|
+
exports.saveBaseline = saveBaseline;
|
|
9
|
+
exports.hasRatchetFailed = hasRatchetFailed;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const DEFAULT_BASELINE_FILE = '.arch-baseline.json';
|
|
13
|
+
/**
|
|
14
|
+
* Resolves the baseline file path.
|
|
15
|
+
* `basePath` may be absolute or project-relative; defaults to `.arch-baseline.json`.
|
|
16
|
+
*/
|
|
17
|
+
function resolveBaselinePath(basePath, projectDir) {
|
|
18
|
+
const p = basePath ?? DEFAULT_BASELINE_FILE;
|
|
19
|
+
return path_1.default.isAbsolute(p) ? p : path_1.default.join(projectDir, p);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reads and parses an existing baseline file.
|
|
23
|
+
* Returns `null` if the file does not exist or cannot be parsed.
|
|
24
|
+
*/
|
|
25
|
+
function loadBaseline(baselinePath) {
|
|
26
|
+
if (!fs_1.default.existsSync(baselinePath))
|
|
27
|
+
return null;
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(fs_1.default.readFileSync(baselinePath, 'utf-8'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Writes a baseline file with the current violation count.
|
|
37
|
+
*/
|
|
38
|
+
function saveBaseline(baselinePath, violationCount, toolVersion) {
|
|
39
|
+
const data = {
|
|
40
|
+
violationCount,
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
version: toolVersion,
|
|
43
|
+
};
|
|
44
|
+
fs_1.default.writeFileSync(baselinePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns `true` when the ratchet should fail — i.e. the current violation
|
|
48
|
+
* count has INCREASED beyond the saved baseline.
|
|
49
|
+
* Returns `false` when violations stayed the same or decreased.
|
|
50
|
+
*/
|
|
51
|
+
function hasRatchetFailed(currentCount, baseline) {
|
|
52
|
+
return currentCount > baseline.violationCount;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":";;;;;AAkBA,kDAGC;AAMD,oCAOC;AAKD,oCAWC;AAOD,4CAEC;AA3DD,4CAAoB;AACpB,gDAAwB;AAWxB,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AAEpD;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,QAA4B,EAAE,UAAkB;IAClF,MAAM,CAAC,GAAG,QAAQ,IAAI,qBAAqB,CAAC;IAC5C,OAAO,cAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,YAAoB;IAC/C,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAiB,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAC1B,YAAoB,EACpB,cAAsB,EACtB,WAAmB;IAEnB,MAAM,IAAI,GAAiB;QACzB,cAAc;QACd,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,WAAW;KACrB,CAAC;IACF,YAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,YAAoB,EAAE,QAAsB;IAC3E,OAAO,YAAY,GAAG,QAAQ,CAAC,cAAc,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CircularDep, FileScan } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Detects circular dependencies between architectural layers using Tarjan's
|
|
4
|
+
* Strongly Connected Components (SCC) algorithm.
|
|
5
|
+
*
|
|
6
|
+
* A cycle exists when layer A (transitively) imports layer B which imports
|
|
7
|
+
* back into layer A — e.g. controller → service → controller.
|
|
8
|
+
*
|
|
9
|
+
* Each SCC with more than one node is reported as a circular dependency.
|
|
10
|
+
* Self-loops (a file importing its own layer) are intentionally ignored.
|
|
11
|
+
*
|
|
12
|
+
* @param scans File scan results produced by `scanProject`.
|
|
13
|
+
* @param layers Declared layer names from the architecture config.
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectCircularDependencies(scans: FileScan[], layers: string[]): CircularDep[];
|
|
16
|
+
//# sourceMappingURL=circularDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circularDetector.d.ts","sourceRoot":"","sources":["../src/circularDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,EAAE,GACf,WAAW,EAAE,CA+Df"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectCircularDependencies = detectCircularDependencies;
|
|
4
|
+
const ruleEngine_1 = require("./ruleEngine");
|
|
5
|
+
/**
|
|
6
|
+
* Detects circular dependencies between architectural layers using Tarjan's
|
|
7
|
+
* Strongly Connected Components (SCC) algorithm.
|
|
8
|
+
*
|
|
9
|
+
* A cycle exists when layer A (transitively) imports layer B which imports
|
|
10
|
+
* back into layer A — e.g. controller → service → controller.
|
|
11
|
+
*
|
|
12
|
+
* Each SCC with more than one node is reported as a circular dependency.
|
|
13
|
+
* Self-loops (a file importing its own layer) are intentionally ignored.
|
|
14
|
+
*
|
|
15
|
+
* @param scans File scan results produced by `scanProject`.
|
|
16
|
+
* @param layers Declared layer names from the architecture config.
|
|
17
|
+
*/
|
|
18
|
+
function detectCircularDependencies(scans, layers) {
|
|
19
|
+
// Build directed adjacency list: layer → set of layers it imports.
|
|
20
|
+
const adj = new Map();
|
|
21
|
+
for (const layer of layers)
|
|
22
|
+
adj.set(layer, new Set());
|
|
23
|
+
for (const scan of scans) {
|
|
24
|
+
const src = (0, ruleEngine_1.detectLayer)(scan.file, layers);
|
|
25
|
+
if (!src)
|
|
26
|
+
continue;
|
|
27
|
+
for (const imp of scan.imports) {
|
|
28
|
+
const tgt = (0, ruleEngine_1.detectLayer)(imp.importPath, layers);
|
|
29
|
+
if (tgt && tgt !== src) {
|
|
30
|
+
adj.get(src).add(tgt);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// ── Tarjan's SCC ────────────────────────────────────────────────────────────
|
|
35
|
+
const index = new Map();
|
|
36
|
+
const lowlink = new Map();
|
|
37
|
+
const onStack = new Set();
|
|
38
|
+
const stack = [];
|
|
39
|
+
const cycles = [];
|
|
40
|
+
let counter = 0;
|
|
41
|
+
const strongconnect = (v) => {
|
|
42
|
+
index.set(v, counter);
|
|
43
|
+
lowlink.set(v, counter);
|
|
44
|
+
counter++;
|
|
45
|
+
stack.push(v);
|
|
46
|
+
onStack.add(v);
|
|
47
|
+
for (const w of adj.get(v)) {
|
|
48
|
+
if (!index.has(w)) {
|
|
49
|
+
strongconnect(w);
|
|
50
|
+
lowlink.set(v, Math.min(lowlink.get(v), lowlink.get(w)));
|
|
51
|
+
}
|
|
52
|
+
else if (onStack.has(w)) {
|
|
53
|
+
lowlink.set(v, Math.min(lowlink.get(v), index.get(w)));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Root of an SCC
|
|
57
|
+
if (lowlink.get(v) === index.get(v)) {
|
|
58
|
+
const scc = [];
|
|
59
|
+
let w;
|
|
60
|
+
do {
|
|
61
|
+
w = stack.pop();
|
|
62
|
+
onStack.delete(w);
|
|
63
|
+
scc.push(w);
|
|
64
|
+
} while (w !== v);
|
|
65
|
+
if (scc.length > 1) {
|
|
66
|
+
// Reverse to restore natural discovery order, then close the cycle.
|
|
67
|
+
const ordered = scc.reverse();
|
|
68
|
+
cycles.push({ cycle: [...ordered, ordered[0]] });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
for (const layer of layers) {
|
|
73
|
+
if (!index.has(layer))
|
|
74
|
+
strongconnect(layer);
|
|
75
|
+
}
|
|
76
|
+
return cycles;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=circularDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circularDetector.js","sourceRoot":"","sources":["../src/circularDetector.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* scan Scan the project for architecture violations
|
|
7
7
|
* init Generate a starter .context.yml from the project's folder structure
|
|
8
8
|
* ci Generate a CI workflow file for architecture linting
|
|
9
|
+
* score Calculate architecture health score
|
|
10
|
+
* badge Generate a shields.io badge for the health score
|
|
9
11
|
*/
|
|
10
12
|
export {};
|
|
11
13
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
|