@ecoma-io/archkeep 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +262 -0
- package/cli.mjs +2792 -0
- package/index.mjs +85 -0
- package/lsp.mjs +81 -0
- package/nx.mjs +24 -0
- package/package.json +81 -0
- package/presets/clean-architecture.json +78 -0
- package/presets/ddd-bounded-contexts.json +88 -0
- package/presets/hexagonal.json +68 -0
- package/presets/layered.json +92 -0
- package/presets/modular-monolith.json +85 -0
- package/presets/vertical-slice.json +68 -0
- package/src/analysis/analyze.mjs +218 -0
- package/src/analysis/contract.md +259 -0
- package/src/analysis/go.mjs +414 -0
- package/src/analysis/manifest-util.mjs +68 -0
- package/src/analysis/python.mjs +1266 -0
- package/src/analysis/registry.mjs +74 -0
- package/src/analysis/rust.mjs +674 -0
- package/src/analysis/source-util.mjs +230 -0
- package/src/analysis/typescript.mjs +1034 -0
- package/src/analysis/vue.mjs +156 -0
- package/src/architecture-intent/intent-fingerprint.mjs +29 -0
- package/src/architecture-intent/judge.mjs +539 -0
- package/src/architecture-intent/model.mjs +703 -0
- package/src/architecture-intent/selectors.mjs +170 -0
- package/src/canonical.mjs +48 -0
- package/src/commands/README.md +266 -0
- package/src/commands/adr.mjs +248 -0
- package/src/commands/check.mjs +989 -0
- package/src/commands/context-command.mjs +212 -0
- package/src/commands/context.mjs +790 -0
- package/src/commands/custom-rules.mjs +428 -0
- package/src/commands/debt.mjs +218 -0
- package/src/commands/diff.mjs +523 -0
- package/src/commands/discover.mjs +159 -0
- package/src/commands/drift.mjs +473 -0
- package/src/commands/edge-constraints.mjs +355 -0
- package/src/commands/explain.mjs +359 -0
- package/src/commands/fitness.mjs +226 -0
- package/src/commands/graph.mjs +297 -0
- package/src/commands/health.mjs +213 -0
- package/src/commands/history.mjs +614 -0
- package/src/commands/impact.mjs +226 -0
- package/src/commands/plan-context-command.mjs +496 -0
- package/src/commands/policy.mjs +138 -0
- package/src/commands/provenance-command.mjs +352 -0
- package/src/commands/provenance.mjs +159 -0
- package/src/commands/reconcile.mjs +219 -0
- package/src/commands/report.mjs +553 -0
- package/src/commands/snapshot-meta.mjs +107 -0
- package/src/commands/waivers.mjs +240 -0
- package/src/config.mjs +1308 -0
- package/src/containment.mjs +234 -0
- package/src/custom-rules/evidence.mjs +340 -0
- package/src/custom-rules/host.mjs +1023 -0
- package/src/custom-rules/values.mjs +43 -0
- package/src/entry-point.mjs +55 -0
- package/src/errors.mjs +36 -0
- package/src/eslint-config.mjs +542 -0
- package/src/go-work.mjs +394 -0
- package/src/governance/adr-registry.mjs +539 -0
- package/src/governance/clock.mjs +69 -0
- package/src/governance/debt-ledger.mjs +274 -0
- package/src/governance/discovery-proposal.mjs +423 -0
- package/src/governance/fitness-registry.mjs +504 -0
- package/src/governance/fitness-rules.mjs +668 -0
- package/src/governance/metrics.mjs +392 -0
- package/src/governance/preset-fingerprints.json +16 -0
- package/src/governance/profile-registry.mjs +366 -0
- package/src/governance/provenance-record.mjs +177 -0
- package/src/governance/reconcile-candidates.mjs +301 -0
- package/src/governance/reconcile-score.mjs +503 -0
- package/src/governance/row-schema.mjs +208 -0
- package/src/governance/verdict.mjs +127 -0
- package/src/governance/waiver.mjs +105 -0
- package/src/graph/create-dependencies.mjs +96 -0
- package/src/intent/intent-manifest.json +347 -0
- package/src/intent/mask-non-code.mjs +640 -0
- package/src/lsp/boundary-config.mjs +225 -0
- package/src/lsp/diagnose.mjs +202 -0
- package/src/lsp/diagnostics.mjs +241 -0
- package/src/lsp/protocol.mjs +215 -0
- package/src/lsp/server.mjs +922 -0
- package/src/lsp/workspace-index.mjs +891 -0
- package/src/nx-json.mjs +95 -0
- package/src/options.mjs +611 -0
- package/src/process.mjs +91 -0
- package/src/providers/moon.mjs +733 -0
- package/src/providers/native/README.md +204 -0
- package/src/providers/native/coverage.mjs +74 -0
- package/src/providers/native/differential.fixtures.mjs +1277 -0
- package/src/providers/native/discover.mjs +431 -0
- package/src/providers/native/graph.mjs +234 -0
- package/src/providers/native/index.mjs +152 -0
- package/src/providers/native/model.mjs +755 -0
- package/src/providers/nx.mjs +178 -0
- package/src/report/README.md +89 -0
- package/src/report/adr-text.mjs +129 -0
- package/src/report/context-text.mjs +109 -0
- package/src/report/debt-text.mjs +105 -0
- package/src/report/diff-text.mjs +219 -0
- package/src/report/discover-text.mjs +186 -0
- package/src/report/drift-text.mjs +194 -0
- package/src/report/envelope-shape.mjs +161 -0
- package/src/report/evidence.mjs +157 -0
- package/src/report/explain-text.mjs +159 -0
- package/src/report/graph-text.mjs +116 -0
- package/src/report/health-text.mjs +123 -0
- package/src/report/history-text.mjs +204 -0
- package/src/report/impact-text.mjs +128 -0
- package/src/report/json.mjs +173 -0
- package/src/report/plan-context-text.mjs +159 -0
- package/src/report/provenance-text.mjs +78 -0
- package/src/report/reconcile-text.mjs +159 -0
- package/src/report/report-text.mjs +264 -0
- package/src/report/sarif.mjs +953 -0
- package/src/report/text.mjs +823 -0
- package/src/report/waivers-text.mjs +100 -0
- package/src/rules/README.md +123 -0
- package/src/rules/index.mjs +962 -0
- package/src/rules/match.mjs +1708 -0
- package/src/rules/messages.mjs +73 -0
- package/src/rules/reachability.mjs +224 -0
- package/src/rules/specifiers.mjs +300 -0
- package/src/rules/tags.mjs +238 -0
- package/src/rules/topology.mjs +333 -0
- package/src/tsconfig-paths.mjs +237 -0
- package/src/verdict.mjs +145 -0
- package/src/workspace.mjs +580 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The native project-model provider: `archkeep.json`, no Nx installed, no
|
|
3
|
+
* Nx-shaped `project.json` required.
|
|
4
|
+
*
|
|
5
|
+
* `../nx.mjs`'s `ProjectModelProvider` is a single call — `readProjectGraph`
|
|
6
|
+
* — because Nx already resolved projects, tags and edges before this package
|
|
7
|
+
* ever asked. Nothing here has done that resolving, so this provider's
|
|
8
|
+
* contract is two calls instead of one: `discover` answers "which projects,
|
|
9
|
+
* and which files does none of them own" from the tree alone, and `buildGraph`
|
|
10
|
+
* turns that plus the import sites analysis found into the same
|
|
11
|
+
* `{nodes, dependencies}` shape `../../rules/index.mjs`'s `evaluate` consumes.
|
|
12
|
+
* The split exists because import-site analysis needs projects to already be
|
|
13
|
+
* known (`../../workspace.mjs`'s `createWorkspace`/`analyzeWorkspace` resolve
|
|
14
|
+
* every relative import against a project list), so a single `readGraph` call
|
|
15
|
+
* would have to hide a whole analysis pass inside a provider — the layer this
|
|
16
|
+
* package's `../../../AGENTS.md` reserves for `../../workspace.mjs` alone.
|
|
17
|
+
*
|
|
18
|
+
* This module imports NOTHING from `../../workspace.mjs`: that file's import
|
|
19
|
+
* graph loads the TypeScript compiler at module scope
|
|
20
|
+
* (`../../process.mjs`'s header), and a workspace with no `.ts` file in it, or no
|
|
21
|
+
* `nx` installed, must still be able to discover its `archkeep.json` projects
|
|
22
|
+
* without paying for a compiler it will never call.
|
|
23
|
+
*/
|
|
24
|
+
import { projectOwning } from "../../analysis/source-util.mjs";
|
|
25
|
+
import { ARCHKEEP_MODEL_FILE, loadNativeModel } from "./model.mjs";
|
|
26
|
+
import { discoverNativeProjects } from "./discover.mjs";
|
|
27
|
+
import { judgeCoverage } from "./coverage.mjs";
|
|
28
|
+
import { buildNativeGraph } from "./graph.mjs";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `projectOf`, over `../../analysis/source-util.mjs`'s `projectOwning` — the
|
|
32
|
+
* SAME longest-root-wins answer `../../workspace.mjs`'s `createWorkspace`
|
|
33
|
+
* gives the Nx path, so a file's owner does not depend on which provider
|
|
34
|
+
* found it.
|
|
35
|
+
*
|
|
36
|
+
* @param {{name: string, root: string}[]} projects
|
|
37
|
+
* @returns {(file: string) => string|undefined}
|
|
38
|
+
*/
|
|
39
|
+
function projectOfFactory(projects) {
|
|
40
|
+
return (file) => projectOwning(projects, file)?.name ?? undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Discovers a native workspace's projects and judges its coverage.
|
|
45
|
+
*
|
|
46
|
+
* Two failure classes, both loud (`../../../../../AGENTS.md`'s invariant): a model
|
|
47
|
+
* defect — thrown by `loadNativeModel` or `discoverNativeProjects` — and a
|
|
48
|
+
* stale coverage waiver, thrown here once discovery and coverage are both
|
|
49
|
+
* known. An unclaimed file, and an unparseable `project.json`
|
|
50
|
+
* (`./discover.mjs`'s `readProjectManifest`), are NOT throws: both become a
|
|
51
|
+
* `fileFailure` — `./coverage.mjs`'s and `./discover.mjs`'s own — that ride
|
|
52
|
+
* together in `DiscoveredWorkspace.failures`, because `../../../cli.mjs`'s
|
|
53
|
+
* existing exit-3 path already turns any non-empty `unchecked` count into
|
|
54
|
+
* that exit code once the failure reaches `../../report/text.mjs`'s
|
|
55
|
+
* `formatFailures` — the same mechanism a language analyzer's own
|
|
56
|
+
* `fileFailure` already uses, so nothing about either path is native-specific.
|
|
57
|
+
*
|
|
58
|
+
* @param {{root: string, files: string[], readFile: (path: string) => string|null}} args
|
|
59
|
+
* @returns {{projects: {name: string, root: string, type: string, tags: string[], tagOrigins: Record<string, string[]>, implicitDependencies: string[], targets: string[]}[], projectOf: (file: string) => string|undefined, model: object, failures: object[], exempted: string[]}}
|
|
60
|
+
* @throws {Error} on a malformed `archkeep.json`, a discovery defect, or a
|
|
61
|
+
* stale `coverage.exempt` row.
|
|
62
|
+
*/
|
|
63
|
+
export function discover({ root, files, readFile }) {
|
|
64
|
+
const model = loadNativeModel(root, { readFile });
|
|
65
|
+
const { projects, failures: discoveryFailures } = discoverNativeProjects({
|
|
66
|
+
root,
|
|
67
|
+
files,
|
|
68
|
+
readFile,
|
|
69
|
+
model,
|
|
70
|
+
});
|
|
71
|
+
const projectOf = projectOfFactory(projects);
|
|
72
|
+
const coverage = judgeCoverage({ files, projectOf, exempt: model.coverage.exempt });
|
|
73
|
+
|
|
74
|
+
if (coverage.stale.length > 0) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`archkeep: ${root}/${ARCHKEEP_MODEL_FILE} describes a workspace that does not match the ` +
|
|
77
|
+
`tree:\n ` +
|
|
78
|
+
coverage.stale
|
|
79
|
+
.map(
|
|
80
|
+
(row) =>
|
|
81
|
+
`coverage.exempt: '${row.path}' matches no unclaimed file — either the files it ` +
|
|
82
|
+
`covered are now owned by a project, or the path was never right`,
|
|
83
|
+
)
|
|
84
|
+
.join("\n "),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// `coverage.exempted` used to stop here: `judgeCoverage` computed which
|
|
89
|
+
// files a `coverage.exempt` row removed from `unclaimed`, and nothing past
|
|
90
|
+
// this function ever read the list back — so a workspace could exempt an
|
|
91
|
+
// unbounded number of files from coverage, forever, with no command, no
|
|
92
|
+
// report line, and no JSON field ever naming a single one of them. An
|
|
93
|
+
// exempted file and a genuinely covered one were byte-for-byte
|
|
94
|
+
// indistinguishable in every surface this tool produces, which is exactly
|
|
95
|
+
// the silent direction `../../../../../AGENTS.md`'s invariant forbids —
|
|
96
|
+
// `check`'s own comment two lines above threatens a stale-exemption throw,
|
|
97
|
+
// but nothing threatened the opposite failure: an exemption nobody could see.
|
|
98
|
+
// `../../commands/context.mjs` threads this onto `CommandContext.analysis`
|
|
99
|
+
// and `../../../cli.mjs`'s `check` reports the count beside every verdict,
|
|
100
|
+
// the same "no violations is a claim about coverage too" treatment the
|
|
101
|
+
// import/file/project counts already get.
|
|
102
|
+
return {
|
|
103
|
+
projects,
|
|
104
|
+
projectOf,
|
|
105
|
+
model,
|
|
106
|
+
failures: [...discoveryFailures, ...coverage.failures],
|
|
107
|
+
exempted: coverage.exempted,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Builds the graph `evaluate()` judges, from a `discover()` result and the
|
|
113
|
+
* import sites analysis found across the discovered projects' files.
|
|
114
|
+
*
|
|
115
|
+
* `workspaceLayout` is threaded from `discovered.model` straight onto the
|
|
116
|
+
* graph object returned — declared-or-absent, never defaulted here, the same
|
|
117
|
+
* rule `./model.mjs`'s `normalizeNativeModel` documents (`../../rules/index.mjs`'s
|
|
118
|
+
* `createContext` is the one place that ever applies `DEFAULT_WORKSPACE_LAYOUT`).
|
|
119
|
+
*
|
|
120
|
+
* `exemptedFiles` threads the same way, from `discovered.exempted`: the
|
|
121
|
+
* concrete files `coverage.exempt` removed from coverage. This is the one
|
|
122
|
+
* seam both faces of a native workspace flow through — this CLI's native
|
|
123
|
+
* branch (`../../../src/commands/context.mjs`) and the language server's
|
|
124
|
+
* (`../../../src/lsp/workspace-index.mjs`) both build their graphs here — so
|
|
125
|
+
* threading it in the provider is what keeps an editor verdict and a
|
|
126
|
+
* `archkeep check` verdict on the same exempt-file import from disagreeing.
|
|
127
|
+
*
|
|
128
|
+
* @param {{discovered: ReturnType<typeof discover>, importSites: object[]}} args
|
|
129
|
+
* @returns {{nodes: Record<string, object>, dependencies: Record<string, object[]>, workspaceLayout?: {appsDir: string, libsDir: string}, exemptedFiles?: string[]}}
|
|
130
|
+
*/
|
|
131
|
+
export function buildGraph({ discovered, importSites }) {
|
|
132
|
+
return buildNativeGraph({
|
|
133
|
+
projects: discovered.projects,
|
|
134
|
+
importSites,
|
|
135
|
+
projectOf: discovered.projectOf,
|
|
136
|
+
workspaceLayout: discovered.model.workspaceLayout,
|
|
137
|
+
exemptedFiles: discovered.exempted,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The `ProjectModelProvider`-family object `../../../cli.mjs` selects when a
|
|
143
|
+
* workspace root carries `archkeep.json` rather than `nx.json` (`../nx.mjs`'s
|
|
144
|
+
* `ProjectModelProvider` doc explains the seam both providers implement, and
|
|
145
|
+
* why this one's shape is two calls rather than `readProjectGraph`'s one).
|
|
146
|
+
*/
|
|
147
|
+
export const nativeProvider = {
|
|
148
|
+
name: "native",
|
|
149
|
+
marker: ARCHKEEP_MODEL_FILE,
|
|
150
|
+
discover,
|
|
151
|
+
buildGraph,
|
|
152
|
+
};
|