@ecoma-io/archkeep 0.18.0 → 0.18.1
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/cli.mjs +18 -2
- package/package.json +1 -1
- package/src/commands/diff.mjs +15 -7
- package/src/commands/explain.mjs +11 -0
- package/src/commands/rules.mjs +3 -1
package/cli.mjs
CHANGED
|
@@ -93,6 +93,11 @@
|
|
|
93
93
|
*/
|
|
94
94
|
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
95
95
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
96
|
+
import { createRequire } from "node:module";
|
|
97
|
+
|
|
98
|
+
const require = createRequire(import.meta.url);
|
|
99
|
+
/*** @type {{name: string, version: string}} */
|
|
100
|
+
const { name: TOOL_NAME, version: TOOL_VERSION } = require("./package.json");
|
|
96
101
|
|
|
97
102
|
import { containmentViolation } from "./src/containment.mjs";
|
|
98
103
|
import { UsageError } from "./src/errors.mjs";
|
|
@@ -2541,9 +2546,10 @@ const DELTA_FLAG_HELP = Object.freeze([
|
|
|
2541
2546
|
key: "capture",
|
|
2542
2547
|
arg: "",
|
|
2543
2548
|
describe: Object.freeze([
|
|
2544
|
-
"
|
|
2549
|
+
"Print an evidence snapshot of the current tree",
|
|
2545
2550
|
"(raw import records, graph, coverage, policy",
|
|
2546
|
-
"fingerprint) for a later delta run to compare against",
|
|
2551
|
+
"fingerprint) for a later delta run to compare against.",
|
|
2552
|
+
"Without --output, the snapshot goes to stdout.",
|
|
2547
2553
|
]),
|
|
2548
2554
|
}),
|
|
2549
2555
|
Object.freeze({
|
|
@@ -3621,10 +3627,15 @@ export async function runCli(argv, env) {
|
|
|
3621
3627
|
// one root-marker read and a clean run pays none.
|
|
3622
3628
|
const help = () => usage(optionsForUsage(cwd));
|
|
3623
3629
|
|
|
3630
|
+
// --help and --version are universal flags, handled before command dispatch.
|
|
3624
3631
|
if (argv[0] === "--help" || argv[0] === "-h") {
|
|
3625
3632
|
env.out(help());
|
|
3626
3633
|
return EXIT.ok;
|
|
3627
3634
|
}
|
|
3635
|
+
if (argv[0] === "--version" || argv[0] === "-v") {
|
|
3636
|
+
env.out(`${TOOL_NAME} ${TOOL_VERSION}`);
|
|
3637
|
+
return EXIT.ok;
|
|
3638
|
+
}
|
|
3628
3639
|
|
|
3629
3640
|
const [maybeCommand, ...maybeRest] = argv;
|
|
3630
3641
|
let commandName;
|
|
@@ -3635,6 +3646,11 @@ export async function runCli(argv, env) {
|
|
|
3635
3646
|
} else if (Object.hasOwn(COMMANDS, maybeCommand)) {
|
|
3636
3647
|
commandName = maybeCommand;
|
|
3637
3648
|
rest = maybeRest;
|
|
3649
|
+
// Subcommand --help: show the main help without "unknown option" error.
|
|
3650
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
3651
|
+
env.out(help());
|
|
3652
|
+
return EXIT.ok;
|
|
3653
|
+
}
|
|
3638
3654
|
} else if (
|
|
3639
3655
|
maybeCommand !== "" &&
|
|
3640
3656
|
existsSync(isAbsolute(maybeCommand) ? maybeCommand : join(cwd, maybeCommand))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecoma-io/archkeep",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Architecture enforcement for polyglot repositories — dependency graphs and module boundaries for Go, Rust, Python, TypeScript, JavaScript, Vue, Java and Kotlin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|
package/src/commands/diff.mjs
CHANGED
|
@@ -114,6 +114,21 @@ export function parseBaseline(text, path) {
|
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
if (!envelope.command) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`archkeep: the baseline snapshot at '${path}' has no 'command' field — it is not a ` +
|
|
120
|
+
`'graph' envelope. diff requires a graph snapshot (from 'graph --format json'). ` +
|
|
121
|
+
`For delta evidence snapshots use 'delta <snapshot>'.`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (envelope.command !== "graph") {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`archkeep: the baseline snapshot at '${path}' is a '${envelope.command}' envelope, not a ` +
|
|
128
|
+
`'graph' envelope — diff requires a graph snapshot as its baseline`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
117
132
|
// A consumer that reads a schemaVersion it does not recognise should refuse
|
|
118
133
|
// to parse the rest (`docs/reference/json-output.md`). This tool IS that
|
|
119
134
|
// consumer when reading a baseline — a future schema version could change
|
|
@@ -127,13 +142,6 @@ export function parseBaseline(text, path) {
|
|
|
127
142
|
);
|
|
128
143
|
}
|
|
129
144
|
|
|
130
|
-
if (envelope.command !== "graph") {
|
|
131
|
-
throw new Error(
|
|
132
|
-
`archkeep: the baseline snapshot at '${path}' is a '${envelope.command}' envelope, not a ` +
|
|
133
|
-
`'graph' envelope — diff requires a graph snapshot as its baseline`,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
145
|
if (!envelope.coverage?.complete) {
|
|
138
146
|
throw new Error(
|
|
139
147
|
`archkeep: the baseline snapshot at '${path}' has incomplete coverage — every "removed" ` +
|
package/src/commands/explain.mjs
CHANGED
|
@@ -97,6 +97,7 @@ import {
|
|
|
97
97
|
resolveDecisionRef,
|
|
98
98
|
stripAdrPrefix,
|
|
99
99
|
} from "../governance/adr-registry.mjs";
|
|
100
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
103
|
* Parses a `file:line:column` site string into its components.
|
|
@@ -354,6 +355,16 @@ export function explainCommand(site, commandContext, config, options = {}) {
|
|
|
354
355
|
|
|
355
356
|
const parsed = parseSite(site);
|
|
356
357
|
|
|
358
|
+
// Normalize the site's sourceFile to a workspace-relative path so it
|
|
359
|
+
// matches the analysis record's sourceFile field (contract.md: workspace-relative).
|
|
360
|
+
// Handles: absolute paths, cwd-relative paths, and backslash separators.
|
|
361
|
+
const rawFile = parsed.sourceFile;
|
|
362
|
+
const normalizedFile = isAbsolute(rawFile)
|
|
363
|
+
? relative(root, rawFile)
|
|
364
|
+
: relative(root, resolve(root, rawFile));
|
|
365
|
+
// Normalize backslash separators (Windows paths) to forward slashes.
|
|
366
|
+
parsed.sourceFile = sep === "\\" ? normalizedFile.replaceAll("\\", "/") : normalizedFile;
|
|
367
|
+
|
|
357
368
|
const notAnalyzed = commandContext.analysis.failures
|
|
358
369
|
.filter(isWholeFileFailure)
|
|
359
370
|
.map(({ sourceFile, reason }) => ({ file: sourceFile, reason }));
|
package/src/commands/rules.mjs
CHANGED
|
@@ -84,7 +84,9 @@ function loadCatalog(catalogPath, cwd) {
|
|
|
84
84
|
|
|
85
85
|
if (!existsSync(resolvedPath)) {
|
|
86
86
|
throw new Error(
|
|
87
|
-
`catalog not found at ${catalogPath} — install @ecoma-io/archkeep-rules
|
|
87
|
+
`catalog not found at ${catalogPath} — install @ecoma-io/archkeep-rules ` +
|
|
88
|
+
`(\`npm install -D @ecoma-io/archkeep-rules\`) or use --catalog to point to a ` +
|
|
89
|
+
`catalog.json file`,
|
|
88
90
|
);
|
|
89
91
|
}
|
|
90
92
|
|