laymos 0.0.2 → 0.0.4
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/package.json +4 -3
- package/dist/architecture/analyze-project/analyze-project.d.ts +0 -9
- package/dist/architecture/analyze-project/analyze-project.d.ts.map +0 -1
- package/dist/architecture/analyze-project/analyze-project.js +0 -47
- package/dist/architecture/analyze-project/index.d.ts +0 -3
- package/dist/architecture/analyze-project/index.d.ts.map +0 -1
- package/dist/architecture/analyze-project/index.js +0 -1
- package/dist/architecture/build-report/build-report.d.ts +0 -7
- package/dist/architecture/build-report/build-report.d.ts.map +0 -1
- package/dist/architecture/build-report/build-report.js +0 -80
- package/dist/architecture/build-report/index.d.ts +0 -2
- package/dist/architecture/build-report/index.d.ts.map +0 -1
- package/dist/architecture/build-report/index.js +0 -1
- package/dist/architecture/errors.d.ts +0 -11
- package/dist/architecture/errors.d.ts.map +0 -1
- package/dist/architecture/errors.js +0 -3
- package/dist/architecture/extract-dependencies/extract-dependencies.d.ts +0 -12
- package/dist/architecture/extract-dependencies/extract-dependencies.d.ts.map +0 -1
- package/dist/architecture/extract-dependencies/extract-dependencies.js +0 -162
- package/dist/architecture/extract-dependencies/index.d.ts +0 -3
- package/dist/architecture/extract-dependencies/index.d.ts.map +0 -1
- package/dist/architecture/extract-dependencies/index.js +0 -1
- package/dist/architecture/resolve-architecture/index.d.ts +0 -3
- package/dist/architecture/resolve-architecture/index.d.ts.map +0 -1
- package/dist/architecture/resolve-architecture/index.js +0 -1
- package/dist/architecture/resolve-architecture/resolve-architecture.d.ts +0 -24
- package/dist/architecture/resolve-architecture/resolve-architecture.d.ts.map +0 -1
- package/dist/architecture/resolve-architecture/resolve-architecture.js +0 -74
- package/dist/architecture/validate-rules/index.d.ts +0 -3
- package/dist/architecture/validate-rules/index.d.ts.map +0 -1
- package/dist/architecture/validate-rules/index.js +0 -1
- package/dist/architecture/validate-rules/validate-rules.d.ts +0 -10
- package/dist/architecture/validate-rules/validate-rules.d.ts.map +0 -1
- package/dist/architecture/validate-rules/validate-rules.js +0 -80
- package/dist/cli/deps/dependency-tree.d.ts +0 -3
- package/dist/cli/deps/dependency-tree.d.ts.map +0 -1
- package/dist/cli/deps/dependency-tree.js +0 -39
- package/dist/cli/deps/deps.d.ts +0 -7
- package/dist/cli/deps/deps.d.ts.map +0 -1
- package/dist/cli/deps/deps.js +0 -26
- package/dist/cli/deps/index.d.ts +0 -2
- package/dist/cli/deps/index.d.ts.map +0 -1
- package/dist/cli/deps/index.js +0 -2
- package/dist/config/define-config.d.ts +0 -8
- package/dist/config/define-config.d.ts.map +0 -1
- package/dist/config/define-config.js +0 -373
- package/dist/config/index.d.ts +0 -11
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -6
- package/dist/config/layer-graph.d.ts +0 -7
- package/dist/config/layer-graph.d.ts.map +0 -1
- package/dist/config/layer-graph.js +0 -26
- package/dist/config/layer.d.ts +0 -5
- package/dist/config/layer.d.ts.map +0 -1
- package/dist/config/layer.js +0 -8
- package/dist/config/load-config/index.d.ts +0 -2
- package/dist/config/load-config/index.d.ts.map +0 -1
- package/dist/config/load-config/index.js +0 -1
- package/dist/config/load-config/load-config.d.ts +0 -34
- package/dist/config/load-config/load-config.d.ts.map +0 -1
- package/dist/config/load-config/load-config.js +0 -127
- package/dist/config/module.d.ts +0 -11
- package/dist/config/module.d.ts.map +0 -1
- package/dist/config/module.js +0 -22
- package/dist/config/path.d.ts +0 -4
- package/dist/config/path.d.ts.map +0 -1
- package/dist/config/path.js +0 -32
- package/dist/config/project-narrative.d.ts +0 -15
- package/dist/config/project-narrative.d.ts.map +0 -1
- package/dist/config/project-narrative.js +0 -11
- package/dist/config/types.d.ts +0 -38
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js +0 -1
- package/dist/domain/architecture-analysis/modules/unexposed-modules.d.ts +0 -3
- package/dist/domain/architecture-analysis/modules/unexposed-modules.d.ts.map +0 -1
- package/dist/domain/architecture-analysis/modules/unexposed-modules.js +0 -10
- package/dist/domain/project-config/validation/modules/containment.d.ts +0 -3
- package/dist/domain/project-config/validation/modules/containment.d.ts.map +0 -1
- package/dist/domain/project-config/validation/modules/containment.js +0 -24
- package/dist/domain/project-config/validation/modules/kinds.d.ts +0 -3
- package/dist/domain/project-config/validation/modules/kinds.d.ts.map +0 -1
- package/dist/domain/project-config/validation/modules/kinds.js +0 -18
- package/dist/domain/project-config/validation/modules/nested.d.ts +0 -3
- package/dist/domain/project-config/validation/modules/nested.d.ts.map +0 -1
- package/dist/domain/project-config/validation/modules/nested.js +0 -34
- package/dist/domain/project-config/validation/modules/subpaths.d.ts +0 -3
- package/dist/domain/project-config/validation/modules/subpaths.d.ts.map +0 -1
- package/dist/domain/project-config/validation/modules/subpaths.js +0 -40
- package/dist/entrypoints/cli/cli.d.ts +0 -3
- package/dist/entrypoints/cli/cli.d.ts.map +0 -1
- package/dist/entrypoints/cli/cli.js +0 -6
- package/dist/entrypoints/cli/run.d.ts +0 -13
- package/dist/entrypoints/cli/run.d.ts.map +0 -1
- package/dist/entrypoints/cli/run.js +0 -155
- package/dist/entrypoints/cli/test-output.d.ts +0 -17
- package/dist/entrypoints/cli/test-output.d.ts.map +0 -1
- package/dist/entrypoints/cli/test-output.js +0 -244
- package/dist/entrypoints/node/index.d.ts +0 -24
- package/dist/entrypoints/node/index.d.ts.map +0 -1
- package/dist/entrypoints/node/index.js +0 -27
- package/dist/entrypoints/report/index.d.ts +0 -2
- package/dist/entrypoints/report/index.d.ts.map +0 -1
- package/dist/entrypoints/report/index.js +0 -1
- package/dist/markdown/index.d.ts +0 -3
- package/dist/markdown/index.d.ts.map +0 -1
- package/dist/markdown/index.js +0 -1
- package/dist/markdown/markdown.d.ts +0 -7
- package/dist/markdown/markdown.d.ts.map +0 -1
- package/dist/markdown/markdown.js +0 -13
- package/dist/orchestrator/query-dependencies/index.d.ts +0 -3
- package/dist/orchestrator/query-dependencies/index.d.ts.map +0 -1
- package/dist/orchestrator/query-dependencies/index.js +0 -4
- package/dist/orchestrator/query-dependencies/query-dependencies.d.ts +0 -12
- package/dist/orchestrator/query-dependencies/query-dependencies.d.ts.map +0 -1
- package/dist/orchestrator/query-dependencies/query-dependencies.js +0 -20
- package/dist/report/architecture.d.ts +0 -104
- package/dist/report/architecture.d.ts.map +0 -1
- package/dist/report/architecture.js +0 -1
- package/dist/report/index.d.ts +0 -4
- package/dist/report/index.d.ts.map +0 -1
- package/dist/report/index.js +0 -2
- package/dist/report/tests.d.ts +0 -411
- package/dist/report/tests.d.ts.map +0 -1
- package/dist/report/tests.js +0 -102
- package/dist/tests/authoring/index.d.ts +0 -72
- package/dist/tests/authoring/index.d.ts.map +0 -1
- package/dist/tests/authoring/index.js +0 -312
- package/dist/tests/run-tests/index.d.ts +0 -3
- package/dist/tests/run-tests/index.d.ts.map +0 -1
- package/dist/tests/run-tests/index.js +0 -1
- package/dist/tests/run-tests/run-tests.d.ts +0 -26
- package/dist/tests/run-tests/run-tests.d.ts.map +0 -1
- package/dist/tests/run-tests/run-tests.js +0 -284
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "laymos",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Enforces architectural dependency rules and explores source dependencies.",
|
|
6
6
|
"repository": {
|
|
@@ -48,11 +48,12 @@
|
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@effect/platform-node": "4.0.0-beta.102",
|
|
51
|
+
"@effect/platform-node-shared": "4.0.0-beta.102",
|
|
51
52
|
"oxc-parser": "0.143.0",
|
|
52
53
|
"oxc-resolver": "11.24.2",
|
|
53
54
|
"tsx": "4.23.1",
|
|
54
55
|
"yoctocolors": "2.2.0",
|
|
55
|
-
"@pkishorez/effect-tracer": "0.0.
|
|
56
|
+
"@pkishorez/effect-tracer": "0.0.2"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@types/node": "26.1.1",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"vitest": "4.1.10"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
|
-
"effect": "
|
|
67
|
+
"effect": "4.0.0-beta.102"
|
|
67
68
|
},
|
|
68
69
|
"publishConfig": {
|
|
69
70
|
"access": "public"
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
import type { LaymosReport } from '../../report/index.js';
|
|
3
|
-
import type { LaymosError } from '../errors.js';
|
|
4
|
-
export interface AnalyzeProjectRequest {
|
|
5
|
-
readonly projectDir: string;
|
|
6
|
-
}
|
|
7
|
-
/** Analyzes one project's declared and actual static architecture. */
|
|
8
|
-
export declare const analyzeProject: ({ projectDir, }: AnalyzeProjectRequest) => Effect.Effect<LaymosReport, LaymosError>;
|
|
9
|
-
//# sourceMappingURL=analyze-project.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"analyze-project.d.ts","sourceRoot":"","sources":["../../../src/architecture/analyze-project/analyze-project.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,sEAAsE;AACtE,eAAO,MAAM,cAAc,oBAExB,qBAAqB,KAAG,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAYrB,CAAC"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
import { Effect } from 'effect';
|
|
4
|
-
import { buildReport } from '../build-report/index.js';
|
|
5
|
-
import { extractFileGraph } from '../extract-dependencies/index.js';
|
|
6
|
-
import { loadConfig } from '../../config/load-config/index.js';
|
|
7
|
-
import { resolveProject } from '../resolve-architecture/index.js';
|
|
8
|
-
import { validateRules } from '../validate-rules/index.js';
|
|
9
|
-
/** Analyzes one project's declared and actual static architecture. */
|
|
10
|
-
export const analyzeProject = ({ projectDir, }) => Effect.gen(function* () {
|
|
11
|
-
const config = yield* loadConfig({ projectDir });
|
|
12
|
-
const warnings = findMissingPathWarnings(projectDir, config);
|
|
13
|
-
const fileGraph = yield* extractFileGraph(projectDir, config.sourceRoots, config.ignore ?? []);
|
|
14
|
-
const resolved = yield* resolveProject(config, fileGraph);
|
|
15
|
-
const validation = yield* validateRules(resolved);
|
|
16
|
-
return yield* buildReport(resolved, validation, warnings);
|
|
17
|
-
}).pipe(Effect.withSpan('project.analyze'));
|
|
18
|
-
function findMissingPathWarnings(projectDir, config) {
|
|
19
|
-
const warnings = [];
|
|
20
|
-
for (const path of config.sourceRoots) {
|
|
21
|
-
if (!existsSync(resolve(projectDir, path))) {
|
|
22
|
-
warnings.push({ kind: 'missing-source-root', path });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
const layers = new Set(config.graphs.flatMap((graph) => [...graph.layers]));
|
|
26
|
-
for (const layer of layers) {
|
|
27
|
-
for (const path of layer.paths) {
|
|
28
|
-
if (!existsSync(resolve(projectDir, path))) {
|
|
29
|
-
warnings.push({
|
|
30
|
-
kind: 'missing-layer-path',
|
|
31
|
-
layer: layer.name,
|
|
32
|
-
path,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
for (const module of config.modules ?? []) {
|
|
38
|
-
if (!existsSync(resolve(projectDir, module.path))) {
|
|
39
|
-
warnings.push({
|
|
40
|
-
kind: 'missing-module-path',
|
|
41
|
-
module: module.path,
|
|
42
|
-
path: module.path,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return warnings;
|
|
47
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/architecture/analyze-project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { analyzeProject } from './analyze-project.js';
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
import type { AnalysisWarning, LaymosReport } from '../../report/index.js';
|
|
3
|
-
import type { ResolvedProject } from '../resolve-architecture/index.js';
|
|
4
|
-
import type { RuleValidation } from '../validate-rules/index.js';
|
|
5
|
-
/** Emits the canonical, serializable result of Laymos analysis. */
|
|
6
|
-
export declare function buildReport(resolved: ResolvedProject, validation: RuleValidation, warnings?: readonly AnalysisWarning[]): Effect.Effect<LaymosReport>;
|
|
7
|
-
//# sourceMappingURL=build-report.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-report.d.ts","sourceRoot":"","sources":["../../../src/architecture/build-report/build-report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAKb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AA2EjE,mEAAmE;AACnE,wBAAgB,WAAW,CACzB,QAAQ,EAAE,eAAe,EACzB,UAAU,EAAE,cAAc,EAC1B,QAAQ,GAAE,SAAS,eAAe,EAAO,GACxC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAQ7B"}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
function emitArchitecture(config) {
|
|
3
|
-
const layers = {};
|
|
4
|
-
for (const graph of config.graphs) {
|
|
5
|
-
for (const layer of graph.layers) {
|
|
6
|
-
layers[layer.name] = {
|
|
7
|
-
paths: layer.paths,
|
|
8
|
-
...(layer.description !== undefined
|
|
9
|
-
? { description: layer.description }
|
|
10
|
-
: {}),
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
const modules = {};
|
|
15
|
-
for (const module of config.modules ?? []) {
|
|
16
|
-
modules[module.path] = {
|
|
17
|
-
...(module.description === undefined
|
|
18
|
-
? {}
|
|
19
|
-
: { description: module.description }),
|
|
20
|
-
...(module.documentation === undefined
|
|
21
|
-
? {}
|
|
22
|
-
: { documentation: module.documentation.content }),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
sourceRoots: config.sourceRoots,
|
|
27
|
-
layers,
|
|
28
|
-
graphs: config.graphs.map((graph) => ({
|
|
29
|
-
name: graph.name,
|
|
30
|
-
layers: graph.layers.map((layer) => layer.name),
|
|
31
|
-
edges: graph.edges.map((edge) => ({
|
|
32
|
-
from: edge.from.name,
|
|
33
|
-
to: edge.to.name,
|
|
34
|
-
})),
|
|
35
|
-
...(graph.description !== undefined
|
|
36
|
-
? { description: graph.description }
|
|
37
|
-
: {}),
|
|
38
|
-
})),
|
|
39
|
-
modules,
|
|
40
|
-
moduleRules: (config.moduleRules ?? []).map((rules) => ({
|
|
41
|
-
module: rules.module.path,
|
|
42
|
-
...(rules.canImport !== undefined
|
|
43
|
-
? { canImport: rules.canImport.map((module) => module.path) }
|
|
44
|
-
: {}),
|
|
45
|
-
...(rules.canImportedBy !== undefined
|
|
46
|
-
? {
|
|
47
|
-
canImportedBy: rules.canImportedBy.map((module) => module.path),
|
|
48
|
-
}
|
|
49
|
-
: {}),
|
|
50
|
-
})),
|
|
51
|
-
ignoredPaths: config.ignore ?? [],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function emitFiles(resolved) {
|
|
55
|
-
const files = {};
|
|
56
|
-
for (const [path, file] of Object.entries(resolved.files)) {
|
|
57
|
-
const imports = resolved.fileGraph.files[path]?.imports ?? [];
|
|
58
|
-
if (file.kind === 'covered') {
|
|
59
|
-
files[path] = {
|
|
60
|
-
kind: 'covered',
|
|
61
|
-
layer: file.layer,
|
|
62
|
-
...(file.module !== undefined ? { module: file.module } : {}),
|
|
63
|
-
imports,
|
|
64
|
-
};
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
files[path] = { kind: file.kind, imports };
|
|
68
|
-
}
|
|
69
|
-
return files;
|
|
70
|
-
}
|
|
71
|
-
/** Emits the canonical, serializable result of Laymos analysis. */
|
|
72
|
-
export function buildReport(resolved, validation, warnings = []) {
|
|
73
|
-
return Effect.succeed({
|
|
74
|
-
architecture: emitArchitecture(resolved.config),
|
|
75
|
-
files: emitFiles(resolved),
|
|
76
|
-
violations: validation.violations,
|
|
77
|
-
coverage: validation.coverage,
|
|
78
|
-
warnings,
|
|
79
|
-
}).pipe(Effect.withSpan('report.build'));
|
|
80
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/architecture/build-report/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { buildReport } from './build-report.js';
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
declare const ExtractError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
2
|
-
readonly _tag: "ExtractError";
|
|
3
|
-
} & Readonly<A>;
|
|
4
|
-
export declare class ExtractError extends ExtractError_base<{
|
|
5
|
-
readonly baseDir: string;
|
|
6
|
-
readonly cause: unknown;
|
|
7
|
-
}> {
|
|
8
|
-
}
|
|
9
|
-
export type LaymosError = import('../config/load-config/index.js').LoadConfigError | ExtractError;
|
|
10
|
-
export {};
|
|
11
|
-
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/architecture/errors.ts"],"names":[],"mappings":";;;AAEA,qBAAa,YAAa,SAAQ,kBAAiC;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;AAEL,MAAM,MAAM,WAAW,GACnB,OAAO,gCAAgC,EAAE,eAAe,GACxD,YAAY,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
import { ExtractError } from '../errors.js';
|
|
3
|
-
export interface FileNode {
|
|
4
|
-
readonly path: string;
|
|
5
|
-
readonly imports: readonly string[];
|
|
6
|
-
}
|
|
7
|
-
export interface FileGraph {
|
|
8
|
-
readonly files: Readonly<Record<string, FileNode>>;
|
|
9
|
-
}
|
|
10
|
-
/** Extracts configured source roots with skott and type-only tracking enabled. */
|
|
11
|
-
export declare function extractFileGraph(baseDir: string, sourceRoots: readonly string[], ignoredPaths?: readonly string[]): Effect.Effect<FileGraph, ExtractError>;
|
|
12
|
-
//# sourceMappingURL=extract-dependencies.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extract-dependencies.d.ts","sourceRoot":"","sources":["../../../src/architecture/extract-dependencies/extract-dependencies.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAIxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAM5C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;CACpD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,YAAY,GAAE,SAAS,MAAM,EAAO,GACnC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAgExC"}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
|
-
import { Effect, Schema } from 'effect';
|
|
4
|
-
import skott from 'skott';
|
|
5
|
-
import { pathContains } from '../../config/path.js';
|
|
6
|
-
import { ExtractError } from '../errors.js';
|
|
7
|
-
const MissingFileErrorSchema = Schema.Struct({
|
|
8
|
-
code: Schema.Literal('ENOENT'),
|
|
9
|
-
});
|
|
10
|
-
/** Extracts configured source roots with skott and type-only tracking enabled. */
|
|
11
|
-
export function extractFileGraph(baseDir, sourceRoots, ignoredPaths = []) {
|
|
12
|
-
return Effect.tryPromise({
|
|
13
|
-
try: async () => {
|
|
14
|
-
const architectureFiles = await listSourceFiles(baseDir, sourceRoots);
|
|
15
|
-
const eligibleFiles = architectureFiles.filter((path) => !ignoredPaths.some((ignored) => pathContains(ignored, path)));
|
|
16
|
-
const eligible = new Set(eligibleFiles);
|
|
17
|
-
const imports = new Map(architectureFiles.map((path) => [path, new Set()]));
|
|
18
|
-
const extracted = new Set();
|
|
19
|
-
for (const sourceRoot of sourceRoots) {
|
|
20
|
-
const absolute = resolve(baseDir, sourceRoot);
|
|
21
|
-
const sourceRootStat = await statOrUndefined(absolute);
|
|
22
|
-
if (sourceRootStat === undefined)
|
|
23
|
-
continue;
|
|
24
|
-
if (sourceRootStat.isFile() && !eligible.has(sourceRoot))
|
|
25
|
-
continue;
|
|
26
|
-
if (!sourceRootStat.isFile() && !sourceRootStat.isDirectory())
|
|
27
|
-
continue;
|
|
28
|
-
const structure = sourceRootStat.isFile()
|
|
29
|
-
? await runSkott({ baseDir, ignoredPaths, entrypoint: absolute })
|
|
30
|
-
: await runSkott({ baseDir, ignoredPaths, cwd: absolute });
|
|
31
|
-
const resolutionBase = sourceRootStat.isFile()
|
|
32
|
-
? dirname(absolute)
|
|
33
|
-
: absolute;
|
|
34
|
-
for (const path of collectExtractedGraph({
|
|
35
|
-
baseDir,
|
|
36
|
-
eligible,
|
|
37
|
-
graph: structure.graph,
|
|
38
|
-
imports,
|
|
39
|
-
resolutionBase,
|
|
40
|
-
})) {
|
|
41
|
-
extracted.add(path);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
for (const path of eligibleFiles) {
|
|
45
|
-
if (extracted.has(path))
|
|
46
|
-
continue;
|
|
47
|
-
const entrypoint = resolve(baseDir, path);
|
|
48
|
-
const structure = await runSkott({
|
|
49
|
-
baseDir,
|
|
50
|
-
ignoredPaths,
|
|
51
|
-
entrypoint,
|
|
52
|
-
});
|
|
53
|
-
collectExtractedGraph({
|
|
54
|
-
baseDir,
|
|
55
|
-
eligible,
|
|
56
|
-
graph: structure.graph,
|
|
57
|
-
imports,
|
|
58
|
-
resolutionBase: dirname(entrypoint),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
files: Object.fromEntries(architectureFiles.map((path) => [
|
|
63
|
-
path,
|
|
64
|
-
{ path, imports: [...imports.get(path)].sort() },
|
|
65
|
-
])),
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
catch: (cause) => new ExtractError({ baseDir, cause }),
|
|
69
|
-
}).pipe(Effect.withSpan('dependencies.extract'));
|
|
70
|
-
}
|
|
71
|
-
async function runSkott({ baseDir, ignoredPaths, entrypoint, cwd, }) {
|
|
72
|
-
const { getStructure } = await skott({
|
|
73
|
-
...(entrypoint === undefined ? { cwd: cwd ?? baseDir } : { entrypoint }),
|
|
74
|
-
ignorePatterns: ignoredPaths.flatMap((path) => {
|
|
75
|
-
const absolute = toPosixPath(resolve(baseDir, path));
|
|
76
|
-
return [absolute, `${absolute}/**`];
|
|
77
|
-
}),
|
|
78
|
-
tsConfigPath: resolve(baseDir, 'tsconfig.json'),
|
|
79
|
-
dependencyTracking: {
|
|
80
|
-
builtin: false,
|
|
81
|
-
thirdParty: false,
|
|
82
|
-
typeOnly: true,
|
|
83
|
-
},
|
|
84
|
-
fileExtensions: [...sourceExtensions],
|
|
85
|
-
incremental: false,
|
|
86
|
-
});
|
|
87
|
-
return getStructure();
|
|
88
|
-
}
|
|
89
|
-
function collectExtractedGraph({ baseDir, eligible, graph, imports, resolutionBase, }) {
|
|
90
|
-
const extracted = new Set();
|
|
91
|
-
for (const node of Object.values(graph)) {
|
|
92
|
-
const from = findEligiblePath(baseDir, node.id, eligible, resolutionBase);
|
|
93
|
-
if (from === undefined)
|
|
94
|
-
continue;
|
|
95
|
-
extracted.add(from);
|
|
96
|
-
for (const adjacent of node.adjacentTo) {
|
|
97
|
-
const to = findEligiblePath(baseDir, adjacent, eligible, resolutionBase);
|
|
98
|
-
if (to !== undefined) {
|
|
99
|
-
imports.get(from).add(to);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return extracted;
|
|
104
|
-
}
|
|
105
|
-
const sourceExtensions = ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx'];
|
|
106
|
-
async function listSourceFiles(baseDir, sourceRoots) {
|
|
107
|
-
const files = new Set();
|
|
108
|
-
for (const sourceRoot of sourceRoots) {
|
|
109
|
-
const absolute = resolve(baseDir, sourceRoot);
|
|
110
|
-
const sourceRootStat = await statOrUndefined(absolute);
|
|
111
|
-
if (sourceRootStat?.isFile()) {
|
|
112
|
-
if (isSupportedSourceFile(sourceRoot))
|
|
113
|
-
files.add(sourceRoot);
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (!sourceRootStat?.isDirectory())
|
|
117
|
-
continue;
|
|
118
|
-
const entries = await readdir(absolute, {
|
|
119
|
-
recursive: true,
|
|
120
|
-
withFileTypes: true,
|
|
121
|
-
});
|
|
122
|
-
for (const entry of entries) {
|
|
123
|
-
if (!entry.isFile() || !isSupportedSourceFile(entry.name))
|
|
124
|
-
continue;
|
|
125
|
-
files.add(toPosixPath(relative(baseDir, join(entry.parentPath, entry.name))));
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return [...files].sort();
|
|
129
|
-
}
|
|
130
|
-
async function statOrUndefined(path) {
|
|
131
|
-
try {
|
|
132
|
-
return await stat(path);
|
|
133
|
-
}
|
|
134
|
-
catch (cause) {
|
|
135
|
-
if (Schema.is(MissingFileErrorSchema)(cause)) {
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
throw cause;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function isSupportedSourceFile(path) {
|
|
142
|
-
return (sourceExtensions.some((extension) => path.endsWith(extension)) &&
|
|
143
|
-
!path.endsWith('.d.ts') &&
|
|
144
|
-
!/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(path) &&
|
|
145
|
-
!/\.min\.(?:[cm]?js|jsx)$/.test(path));
|
|
146
|
-
}
|
|
147
|
-
function findEligiblePath(baseDir, path, eligible, resolutionBase) {
|
|
148
|
-
const absoluteCandidates = isAbsolute(path)
|
|
149
|
-
? [path]
|
|
150
|
-
: [
|
|
151
|
-
resolve(process.cwd(), path),
|
|
152
|
-
...(resolutionBase === undefined
|
|
153
|
-
? []
|
|
154
|
-
: [resolve(resolutionBase, path)]),
|
|
155
|
-
];
|
|
156
|
-
return absoluteCandidates
|
|
157
|
-
.map((candidate) => toPosixPath(relative(baseDir, candidate)))
|
|
158
|
-
.find((candidate) => eligible.has(candidate));
|
|
159
|
-
}
|
|
160
|
-
function toPosixPath(path) {
|
|
161
|
-
return sep === '/' ? path : path.split(sep).join('/');
|
|
162
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/architecture/extract-dependencies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { extractFileGraph } from './extract-dependencies.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/architecture/resolve-architecture/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { resolveProject } from './resolve-architecture.js';
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
import type { LaymosConfig } from '../../config/types.js';
|
|
3
|
-
import type { FileGraph } from '../extract-dependencies/index.js';
|
|
4
|
-
export type ResolvedFile = {
|
|
5
|
-
readonly kind: 'ignored';
|
|
6
|
-
readonly path: string;
|
|
7
|
-
} | {
|
|
8
|
-
readonly kind: 'uncovered';
|
|
9
|
-
readonly path: string;
|
|
10
|
-
} | {
|
|
11
|
-
readonly kind: 'covered';
|
|
12
|
-
readonly path: string;
|
|
13
|
-
readonly layer: string;
|
|
14
|
-
readonly module?: string;
|
|
15
|
-
};
|
|
16
|
-
export interface ResolvedProject {
|
|
17
|
-
readonly config: LaymosConfig;
|
|
18
|
-
readonly fileGraph: FileGraph;
|
|
19
|
-
readonly files: Readonly<Record<string, ResolvedFile>>;
|
|
20
|
-
readonly reachability: Readonly<Record<string, readonly string[]>>;
|
|
21
|
-
}
|
|
22
|
-
/** Assigns files to layers and modules; validation happens in defineConfig. */
|
|
23
|
-
export declare function resolveProject(config: LaymosConfig, fileGraph: FileGraph): Effect.Effect<ResolvedProject>;
|
|
24
|
-
//# sourceMappingURL=resolve-architecture.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-architecture.d.ts","sourceRoot":"","sources":["../../../src/architecture/resolve-architecture/resolve-architecture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,kCAAkC,CAAC;AAE5E,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IACvD,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;CACpE;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,SAAS,GACnB,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CA4DhC"}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
import { pathContains } from '../../config/path.js';
|
|
3
|
-
/** Assigns files to layers and modules; validation happens in defineConfig. */
|
|
4
|
-
export function resolveProject(config, fileGraph) {
|
|
5
|
-
const layers = [
|
|
6
|
-
...new Set(config.graphs.flatMap((graph) => [...graph.layers])),
|
|
7
|
-
];
|
|
8
|
-
const files = {};
|
|
9
|
-
for (const path of Object.keys(fileGraph.files).sort()) {
|
|
10
|
-
if ((config.ignore ?? []).some((ignored) => pathContains(ignored, path))) {
|
|
11
|
-
files[path] = { kind: 'ignored', path };
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
const layer = layers
|
|
15
|
-
.flatMap((candidate) => candidate.paths
|
|
16
|
-
.filter((prefix) => pathContains(prefix, path))
|
|
17
|
-
.map((prefix) => ({ candidate, prefix })))
|
|
18
|
-
.sort((a, b) => b.prefix.length - a.prefix.length)[0]?.candidate;
|
|
19
|
-
if (layer === undefined) {
|
|
20
|
-
files[path] = { kind: 'uncovered', path };
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const module = (config.modules ?? []).find((candidate) => pathContains(candidate.path, path));
|
|
24
|
-
files[path] = {
|
|
25
|
-
kind: 'covered',
|
|
26
|
-
path,
|
|
27
|
-
layer: layer.name,
|
|
28
|
-
...(module !== undefined ? { module: module.path } : {}),
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const visibleFileGraph = {
|
|
32
|
-
files: Object.fromEntries(Object.entries(fileGraph.files).map(([path, node]) => [
|
|
33
|
-
path,
|
|
34
|
-
{
|
|
35
|
-
path,
|
|
36
|
-
imports: files[path]?.kind === 'ignored'
|
|
37
|
-
? []
|
|
38
|
-
: node.imports.filter((target) => files[target]?.kind !== 'ignored'),
|
|
39
|
-
},
|
|
40
|
-
])),
|
|
41
|
-
};
|
|
42
|
-
return Effect.succeed({
|
|
43
|
-
config,
|
|
44
|
-
fileGraph: visibleFileGraph,
|
|
45
|
-
files,
|
|
46
|
-
reachability: buildReachability(config),
|
|
47
|
-
}).pipe(Effect.withSpan('architecture.resolve'));
|
|
48
|
-
}
|
|
49
|
-
function buildReachability(config) {
|
|
50
|
-
const adjacency = new Map();
|
|
51
|
-
for (const graph of config.graphs) {
|
|
52
|
-
for (const layer of graph.layers) {
|
|
53
|
-
if (!adjacency.has(layer.name))
|
|
54
|
-
adjacency.set(layer.name, new Set());
|
|
55
|
-
}
|
|
56
|
-
for (const edge of graph.edges) {
|
|
57
|
-
adjacency.get(edge.from.name).add(edge.to.name);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const reachability = {};
|
|
61
|
-
for (const layer of adjacency.keys()) {
|
|
62
|
-
const reached = new Set();
|
|
63
|
-
const pending = [...(adjacency.get(layer) ?? [])];
|
|
64
|
-
while (pending.length > 0) {
|
|
65
|
-
const next = pending.pop();
|
|
66
|
-
if (reached.has(next))
|
|
67
|
-
continue;
|
|
68
|
-
reached.add(next);
|
|
69
|
-
pending.push(...(adjacency.get(next) ?? []));
|
|
70
|
-
}
|
|
71
|
-
reachability[layer] = [...reached].sort();
|
|
72
|
-
}
|
|
73
|
-
return reachability;
|
|
74
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/architecture/validate-rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { validateRules } from './validate-rules.js';
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
import type { Coverage, LayerCoverage, LayerViolation, ModuleCoverage, ModuleViolation, Violation } from '../../report/index.js';
|
|
3
|
-
import type { ResolvedProject } from '../resolve-architecture/index.js';
|
|
4
|
-
export type { Coverage, LayerCoverage, LayerViolation, ModuleCoverage, ModuleViolation, Violation, };
|
|
5
|
-
export interface RuleValidation {
|
|
6
|
-
readonly violations: readonly Violation[];
|
|
7
|
-
readonly coverage: Coverage;
|
|
8
|
-
}
|
|
9
|
-
export declare function validateRules(resolved: ResolvedProject): Effect.Effect<RuleValidation>;
|
|
10
|
-
//# sourceMappingURL=validate-rules.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate-rules.d.ts","sourceRoot":"","sources":["../../../src/architecture/validate-rules/validate-rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,SAAS,EACV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAExE,YAAY,EACV,QAAQ,EACR,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,SAAS,GACV,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,eAAe,GACxB,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAoG/B"}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { Effect } from 'effect';
|
|
2
|
-
export function validateRules(resolved) {
|
|
3
|
-
const violations = [];
|
|
4
|
-
const rules = new Map((resolved.config.moduleRules ?? []).map((rule) => [rule.module.path, rule]));
|
|
5
|
-
for (const fromPath of Object.keys(resolved.fileGraph.files).sort()) {
|
|
6
|
-
const from = resolved.files[fromPath];
|
|
7
|
-
if (from?.kind !== 'covered')
|
|
8
|
-
continue;
|
|
9
|
-
for (const toPath of resolved.fileGraph.files[fromPath].imports) {
|
|
10
|
-
const to = resolved.files[toPath];
|
|
11
|
-
if (to?.kind !== 'covered')
|
|
12
|
-
continue;
|
|
13
|
-
if (from.layer !== to.layer &&
|
|
14
|
-
!(resolved.reachability[from.layer] ?? []).includes(to.layer)) {
|
|
15
|
-
violations.push({
|
|
16
|
-
kind: 'layer',
|
|
17
|
-
from: { layer: from.layer, file: fromPath },
|
|
18
|
-
to: { layer: to.layer, file: toPath },
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
if (from.module === undefined ||
|
|
22
|
-
to.module === undefined ||
|
|
23
|
-
from.module === to.module) {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
const fromRule = rules.get(from.module);
|
|
27
|
-
if (fromRule?.canImport !== undefined &&
|
|
28
|
-
!fromRule.canImport.some((module) => module.path === to.module)) {
|
|
29
|
-
violations.push({
|
|
30
|
-
kind: 'module',
|
|
31
|
-
rule: 'canImport',
|
|
32
|
-
from: { module: from.module, layer: from.layer, file: fromPath },
|
|
33
|
-
to: { module: to.module, layer: to.layer, file: toPath },
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
const toRule = rules.get(to.module);
|
|
37
|
-
if (toRule?.canImportedBy !== undefined &&
|
|
38
|
-
!toRule.canImportedBy.some((module) => module.path === from.module)) {
|
|
39
|
-
violations.push({
|
|
40
|
-
kind: 'module',
|
|
41
|
-
rule: 'canImportedBy',
|
|
42
|
-
from: { module: from.module, layer: from.layer, file: fromPath },
|
|
43
|
-
to: { module: to.module, layer: to.layer, file: toPath },
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const visibleFiles = Object.values(resolved.files).filter((file) => file.kind !== 'ignored');
|
|
49
|
-
const coveredFiles = visibleFiles.filter((file) => file.kind === 'covered');
|
|
50
|
-
const uncoveredFiles = visibleFiles
|
|
51
|
-
.filter((file) => file.kind === 'uncovered')
|
|
52
|
-
.map((file) => file.path)
|
|
53
|
-
.sort();
|
|
54
|
-
const layerNames = [
|
|
55
|
-
...new Set(resolved.config.graphs.flatMap((graph) => graph.layers.map((layer) => layer.name))),
|
|
56
|
-
];
|
|
57
|
-
return Effect.succeed({
|
|
58
|
-
violations,
|
|
59
|
-
coverage: {
|
|
60
|
-
layers: {
|
|
61
|
-
totalFiles: visibleFiles.length,
|
|
62
|
-
coveredFiles: coveredFiles.length,
|
|
63
|
-
uncovered: uncoveredFiles,
|
|
64
|
-
},
|
|
65
|
-
modules: layerNames.map((layer) => {
|
|
66
|
-
const layerFiles = coveredFiles.filter((file) => file.layer === layer);
|
|
67
|
-
const uncovered = layerFiles
|
|
68
|
-
.filter((file) => file.module === undefined)
|
|
69
|
-
.map((file) => file.path)
|
|
70
|
-
.sort();
|
|
71
|
-
return {
|
|
72
|
-
layer,
|
|
73
|
-
totalFiles: layerFiles.length,
|
|
74
|
-
coveredFiles: layerFiles.length - uncovered.length,
|
|
75
|
-
uncovered,
|
|
76
|
-
};
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
|
-
}).pipe(Effect.withSpan('architecture.validate'));
|
|
80
|
-
}
|