@coldtea/pr-lens-cli 0.1.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 +21 -0
- package/README.md +117 -0
- package/dist/args.d.ts +18 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +49 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +8 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +97 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/analyze.d.ts +4 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +158 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/comment.d.ts +4 -0
- package/dist/commands/comment.d.ts.map +1 -0
- package/dist/commands/comment.js +63 -0
- package/dist/commands/comment.js.map +1 -0
- package/dist/commands/export.d.ts +4 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +44 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/render.d.ts +4 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +108 -0
- package/dist/commands/render.js.map +1 -0
- package/dist/commands/validate.d.ts +4 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +49 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/comment.d.ts +16 -0
- package/dist/comment.d.ts.map +1 -0
- package/dist/comment.js +148 -0
- package/dist/comment.js.map +1 -0
- package/dist/config-file.d.ts +10 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +19 -0
- package/dist/config-file.js.map +1 -0
- package/dist/corrections.d.ts +16 -0
- package/dist/corrections.d.ts.map +1 -0
- package/dist/corrections.js +80 -0
- package/dist/corrections.js.map +1 -0
- package/dist/document.d.ts +23 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +75 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract.d.ts +54 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +84 -0
- package/dist/extract.js.map +1 -0
- package/dist/git.d.ts +40 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +104 -0
- package/dist/git.js.map +1 -0
- package/dist/io.d.ts +6 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +35 -0
- package/dist/io.js.map +1 -0
- package/dist/prompt.d.ts +23 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +69 -0
- package/dist/prompt.js.map +1 -0
- package/dist/providers/gemini.d.ts +3 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +46 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/http.d.ts +5 -0
- package/dist/providers/http.d.ts.map +1 -0
- package/dist/providers/http.js +26 -0
- package/dist/providers/http.js.map +1 -0
- package/dist/providers/index.d.ts +48 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +71 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +17 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +42 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/snapshot.d.ts +15 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +77 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/terminal.d.ts +6 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +5 -0
- package/dist/terminal.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/src/args.ts +52 -0
- package/src/bin.ts +10 -0
- package/src/cli.ts +120 -0
- package/src/commands/analyze.ts +206 -0
- package/src/commands/comment.ts +77 -0
- package/src/commands/export.ts +61 -0
- package/src/commands/render.ts +125 -0
- package/src/commands/validate.ts +57 -0
- package/src/comment.ts +190 -0
- package/src/config-file.ts +26 -0
- package/src/corrections.ts +86 -0
- package/src/document.ts +119 -0
- package/src/errors.ts +36 -0
- package/src/extract.ts +136 -0
- package/src/git.ts +151 -0
- package/src/io.ts +38 -0
- package/src/prompt.ts +87 -0
- package/src/providers/gemini.ts +61 -0
- package/src/providers/http.ts +54 -0
- package/src/providers/index.ts +124 -0
- package/src/providers/openai.ts +68 -0
- package/src/snapshot.ts +102 -0
- package/src/terminal.ts +9 -0
- package/src/version.ts +4 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { expectOne, parseOptions, readString } from "../args.js";
|
|
2
|
+
import { invalidDocument, readGraphDoc } from "../document.js";
|
|
3
|
+
import { PrLensCliError, usageError } from "../errors.js";
|
|
4
|
+
import { writeJsonFile } from "../io.js";
|
|
5
|
+
import { toStoredMap } from "../snapshot.js";
|
|
6
|
+
const DEFAULT_OUT = ".github/pr-lens.map.json";
|
|
7
|
+
export const USAGE = `pr-lens export <graph.json> [options]
|
|
8
|
+
|
|
9
|
+
Turns a pull-request document into the stored map of the system once that pull
|
|
10
|
+
request has merged: elements the change deletes are dropped, the rest stops
|
|
11
|
+
being annotated, and the result is stamped with the single commit it reflects.
|
|
12
|
+
|
|
13
|
+
The map is a snapshot, not a source of truth — nothing reads it back into the
|
|
14
|
+
pipeline. Committing it gives a repository something to read, to diff, and to
|
|
15
|
+
hand an agent.
|
|
16
|
+
|
|
17
|
+
-o, --out <file> where to write it (default ${DEFAULT_OUT})
|
|
18
|
+
--id <id> map id, so a patch can name it (default <owner>/<name>)
|
|
19
|
+
--sha <sha> the commit it reflects, in full (default the document's head)`;
|
|
20
|
+
export const exportCommand = async (args, terminal) => {
|
|
21
|
+
const { values, positionals } = parseOptions(args, {
|
|
22
|
+
out: { type: "string", short: "o" },
|
|
23
|
+
id: { type: "string" },
|
|
24
|
+
sha: { type: "string" },
|
|
25
|
+
});
|
|
26
|
+
const source = expectOne(positionals, "one graph document to export");
|
|
27
|
+
if (/^https?:\/\//.test(source))
|
|
28
|
+
throw usageError("export reads a local graph document", "fetching a map from a hosted pipeline is not something the CLI does yet");
|
|
29
|
+
const graph = await readGraphDoc(source);
|
|
30
|
+
const out = readString(values.out, "out") ?? DEFAULT_OUT;
|
|
31
|
+
const stored = toStoredMap(graph, {
|
|
32
|
+
id: readString(values.id, "id") ?? `${graph.provenance.repo.owner}/${graph.provenance.repo.name}`,
|
|
33
|
+
sha: readString(values.sha, "sha") ?? graph.provenance.head.sha,
|
|
34
|
+
generatedAt: new Date().toISOString(),
|
|
35
|
+
});
|
|
36
|
+
if (!stored.ok) {
|
|
37
|
+
if (stored.error.code === "NOT_A_SNAPSHOT" || stored.error.code === "PATCH_CONFLICT")
|
|
38
|
+
throw new PrLensCliError("INVALID_DOCUMENT", `${source} cannot be exported as a stored map [${stored.error.code}]`, stored.error.message);
|
|
39
|
+
throw invalidDocument(source, "graph", stored.error);
|
|
40
|
+
}
|
|
41
|
+
const written = await writeJsonFile(out, stored.value);
|
|
42
|
+
terminal.out(`✓ ${written} — stored map of ${stored.value.provenance.repo.owner}/${stored.value.provenance.repo.name} at ${stored.value.provenance.head.sha.slice(0, 7)} · ${stored.value.nodes.length} nodes, ${stored.value.edges.length} edges`);
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAE/C,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;kDAU6B,WAAW;;mFAEsB,CAAC;AAEpF,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,IAAuB,EAAE,QAAkB,EAAiB,EAAE;IAChG,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE;QACjD,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;QACnC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACtB,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACxB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,8BAA8B,CAAC,CAAC;IACtE,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAC7B,MAAM,UAAU,CACd,qCAAqC,EACrC,yEAAyE,CAC1E,CAAC;IAEJ,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,WAAW,CAAC;IAEzD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE;QAChC,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE;QACjG,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG;QAC/D,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAClF,MAAM,IAAI,cAAc,CACtB,kBAAkB,EAClB,GAAG,MAAM,wCAAwC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,EACrE,MAAM,CAAC,KAAK,CAAC,OAAO,CACrB,CAAC;QACJ,MAAM,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CACV,KAAK,OAAO,oBAAoB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,WAAW,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,QAAQ,CACtO,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Terminal } from "../terminal.js";
|
|
2
|
+
export declare const USAGE = "pr-lens render <graph.json> [options]\n\nDraws the document as self-contained SVGs \u2014 one per drill-down section per\nlens, in light and dark \u2014 and writes the manifest describing them. A document\nwith no sections gets one diagram per lens it declares.\n\n -o, --out <dir> where the SVGs and the manifest go (default pr-lens/)\n --theme <theme> light | dark | both (default both)\n --config <file> corrections to draw with (default the repository's, if any)\n --no-config ignore the repository's corrections";
|
|
3
|
+
export declare const renderCommand: (args: readonly string[], terminal: Terminal) => Promise<void>;
|
|
4
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAgB/C,eAAO,MAAM,KAAK,qiBASyC,CAAC;AA6B5D,eAAO,MAAM,aAAa,SAAgB,SAAS,MAAM,EAAE,YAAY,QAAQ,KAAG,OAAO,CAAC,IAAI,CA4D7F,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { applyCorrections, PrLensRenderError, renderAll, THEMES } from "@coldtea/pr-lens-renderer";
|
|
2
|
+
import { safeParseGraphDoc } from "@coldtea/pr-lens-schema";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { expectOne, parseOptions, readBoolean, readString } from "../args.js";
|
|
5
|
+
import { discoverConfig, loadConfig } from "../config-file.js";
|
|
6
|
+
import { unmatchedCorrections } from "../corrections.js";
|
|
7
|
+
import { readGraphDoc } from "../document.js";
|
|
8
|
+
import { PrLensCliError, usageError } from "../errors.js";
|
|
9
|
+
import { repositoryRoot } from "../git.js";
|
|
10
|
+
import { writeJsonFile, writeTextFile } from "../io.js";
|
|
11
|
+
const DEFAULT_OUT = "pr-lens";
|
|
12
|
+
const MANIFEST = "manifest.json";
|
|
13
|
+
/**
|
|
14
|
+
* The document as it was drawn, written beside the manifest.
|
|
15
|
+
*
|
|
16
|
+
* Corrections are applied here, so the document on the way in and the
|
|
17
|
+
* diagrams on the way out can describe different systems — a view whose every
|
|
18
|
+
* element was excluded is drawn by neither. Whatever composes the comment has
|
|
19
|
+
* to read the one the pictures came from, and it is a different file from the
|
|
20
|
+
* one analyze wrote so that neither overwrites the other.
|
|
21
|
+
*/
|
|
22
|
+
const DRAWN = "drawn.graph.json";
|
|
23
|
+
export const USAGE = `pr-lens render <graph.json> [options]
|
|
24
|
+
|
|
25
|
+
Draws the document as self-contained SVGs — one per drill-down section per
|
|
26
|
+
lens, in light and dark — and writes the manifest describing them. A document
|
|
27
|
+
with no sections gets one diagram per lens it declares.
|
|
28
|
+
|
|
29
|
+
-o, --out <dir> where the SVGs and the manifest go (default ${DEFAULT_OUT}/)
|
|
30
|
+
--theme <theme> light | dark | both (default both)
|
|
31
|
+
--config <file> corrections to draw with (default the repository's, if any)
|
|
32
|
+
--no-config ignore the repository's corrections`;
|
|
33
|
+
const readThemes = (value) => {
|
|
34
|
+
const theme = readString(value, "theme") ?? "both";
|
|
35
|
+
switch (theme) {
|
|
36
|
+
case "light":
|
|
37
|
+
case "dark":
|
|
38
|
+
return [theme];
|
|
39
|
+
case "both":
|
|
40
|
+
return THEMES;
|
|
41
|
+
default:
|
|
42
|
+
throw usageError(`--theme takes light, dark or both, got ${JSON.stringify(theme)}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The overlay is applied here rather than by passing the config into
|
|
47
|
+
* `renderAll`, which would apply the same function to the same document — but
|
|
48
|
+
* out of reach, and the comment has to be composed from what was drawn.
|
|
49
|
+
*/
|
|
50
|
+
const correct = (graph, config) => {
|
|
51
|
+
try {
|
|
52
|
+
return applyCorrections(graph, config.map);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!(error instanceof PrLensRenderError))
|
|
56
|
+
throw error;
|
|
57
|
+
throw new PrLensCliError("RENDER_FAILED", `${error.message} [${error.code}]`);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
export const renderCommand = async (args, terminal) => {
|
|
61
|
+
const { values, positionals } = parseOptions(args, {
|
|
62
|
+
out: { type: "string", short: "o" },
|
|
63
|
+
theme: { type: "string" },
|
|
64
|
+
config: { type: "string" },
|
|
65
|
+
"no-config": { type: "boolean" },
|
|
66
|
+
});
|
|
67
|
+
const graph = await readGraphDoc(expectOne(positionals, "one graph document to render"));
|
|
68
|
+
const out = readString(values.out, "out") ?? DEFAULT_OUT;
|
|
69
|
+
const configPath = readString(values.config, "config");
|
|
70
|
+
const configured = readBoolean(values["no-config"])
|
|
71
|
+
? undefined
|
|
72
|
+
: configPath === undefined
|
|
73
|
+
? await discoverConfig(await repositoryRoot(process.cwd()).catch(() => process.cwd()))
|
|
74
|
+
: await loadConfig(configPath);
|
|
75
|
+
const themes = readThemes(values.theme);
|
|
76
|
+
const drawn = (() => {
|
|
77
|
+
if (configured === undefined)
|
|
78
|
+
return graph;
|
|
79
|
+
for (const warning of unmatchedCorrections(graph, configured.config))
|
|
80
|
+
terminal.err(`${configured.path}: ${warning}`);
|
|
81
|
+
const corrected = correct(graph, configured.config);
|
|
82
|
+
const parsed = safeParseGraphDoc(corrected);
|
|
83
|
+
if (!parsed.ok)
|
|
84
|
+
throw new PrLensCliError(`INVALID_DOCUMENT`, `${configured.path} leaves a document that no longer validates [${parsed.error.code}]`, parsed.error.message);
|
|
85
|
+
return parsed.value;
|
|
86
|
+
})();
|
|
87
|
+
const rendered = (() => {
|
|
88
|
+
try {
|
|
89
|
+
return renderAll(drawn, { themes });
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (!(error instanceof PrLensRenderError))
|
|
93
|
+
throw error;
|
|
94
|
+
throw new PrLensCliError("RENDER_FAILED", `${error.message} [${error.code}]`);
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
for (const drawing of rendered.assets) {
|
|
98
|
+
const { id, path } = drawing.asset;
|
|
99
|
+
if (path === undefined)
|
|
100
|
+
throw new PrLensCliError("RENDER_FAILED", `the render named no file for asset '${id}'`);
|
|
101
|
+
await writeTextFile(join(out, path), drawing.svg);
|
|
102
|
+
}
|
|
103
|
+
await writeJsonFile(join(out, DRAWN), drawn);
|
|
104
|
+
await writeJsonFile(join(out, MANIFEST), rendered.manifest);
|
|
105
|
+
const diagrams = new Set(rendered.assets.map((asset) => asset.asset.view ?? asset.lens));
|
|
106
|
+
terminal.out(`✓ ${join(out, MANIFEST)} — ${rendered.assets.length} SVGs across ${diagrams.size} ${diagrams.size === 1 ? "diagram" : "diagrams"}${configured === undefined ? "" : `, corrected by ${configured.path}`}`);
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Terminal } from "../terminal.js";
|
|
2
|
+
export declare const USAGE = "pr-lens validate <file...>\n\nParses graph documents, patch documents, render manifests and configs \u2014 JSON or\nYAML \u2014 and reports every problem in each, rather than only the first. A file\nwithout a 'kind' field is read as a config.";
|
|
3
|
+
export declare const validateCommand: (args: readonly string[], terminal: Terminal) => Promise<void>;
|
|
4
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AA0B/C,eAAO,MAAM,KAAK,uPAI0B,CAAC;AAE7C,eAAO,MAAM,eAAe,SAAgB,SAAS,MAAM,EAAE,YAAY,QAAQ,KAAG,OAAO,CAAC,IAAI,CAoB/F,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { assertNever } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { parseOptions } from "../args.js";
|
|
3
|
+
import { validateDocumentFile } from "../document.js";
|
|
4
|
+
import { formatError, PrLensCliError, usageError } from "../errors.js";
|
|
5
|
+
const count = (n, singular) => `${n} ${n === 1 ? singular : `${singular}s`}`;
|
|
6
|
+
const describe = (validated) => {
|
|
7
|
+
switch (validated.kind) {
|
|
8
|
+
case "graph": {
|
|
9
|
+
const { lanes, nodes, edges, flows, lenses } = validated.document;
|
|
10
|
+
return `graph document · ${count(lanes.length, "lane")}, ${count(nodes.length, "node")}, ${count(edges.length, "edge")}, ${count(flows.length, "flow")} · ${lenses.join(", ")}`;
|
|
11
|
+
}
|
|
12
|
+
case "patch":
|
|
13
|
+
return `patch document · ${count(validated.document.ops.length, "operation")} · ${validated.document.target.fromSha.slice(0, 7)} → ${validated.document.target.toSha.slice(0, 7)}`;
|
|
14
|
+
case "render-manifest":
|
|
15
|
+
return `render manifest · ${count(validated.document.assets.length, "asset")}`;
|
|
16
|
+
case "config": {
|
|
17
|
+
const { map, lenses } = validated.document;
|
|
18
|
+
const corrections = map.rename.length + map.exclude.length + map.lane.length + map.group.length;
|
|
19
|
+
return `config · ${lenses.join(", ")} · ${count(corrections, "correction")}`;
|
|
20
|
+
}
|
|
21
|
+
default:
|
|
22
|
+
return assertNever(validated, "Unhandled document");
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export const USAGE = `pr-lens validate <file...>
|
|
26
|
+
|
|
27
|
+
Parses graph documents, patch documents, render manifests and configs — JSON or
|
|
28
|
+
YAML — and reports every problem in each, rather than only the first. A file
|
|
29
|
+
without a 'kind' field is read as a config.`;
|
|
30
|
+
export const validateCommand = async (args, terminal) => {
|
|
31
|
+
const { positionals } = parseOptions(args, {});
|
|
32
|
+
if (positionals.length === 0)
|
|
33
|
+
throw usageError("expected at least one file to validate");
|
|
34
|
+
let failed = 0;
|
|
35
|
+
for (const path of positionals) {
|
|
36
|
+
try {
|
|
37
|
+
terminal.out(`✓ ${path} — ${describe(await validateDocumentFile(path))}`);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (!(error instanceof PrLensCliError))
|
|
41
|
+
throw error;
|
|
42
|
+
failed += 1;
|
|
43
|
+
terminal.err(formatError(error));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (failed > 0)
|
|
47
|
+
throw new PrLensCliError("INVALID_DOCUMENT", `${failed} of ${count(positionals.length, "document")} did not validate`);
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAA0B,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAGvE,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,QAAgB,EAAU,EAAE,CACpD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,EAAE,CAAC;AAEhD,MAAM,QAAQ,GAAG,CAAC,SAA4B,EAAU,EAAE;IACxD,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,OAAO,EAAE,CAAC;YACb,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC;YAClE,OAAO,oBAAoB,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAClL,CAAC;QACD,KAAK,OAAO;YACV,OAAO,oBAAoB,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACrL,KAAK,iBAAiB;YACpB,OAAO,qBAAqB,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QACjF,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC;YAC3C,MAAM,WAAW,GACf,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;YAC9E,OAAO,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC;QAC/E,CAAC;QACD;YACE,OAAO,WAAW,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACxD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG;;;;4CAIuB,CAAC;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,IAAuB,EAAE,QAAkB,EAAiB,EAAE;IAClG,MAAM,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,UAAU,CAAC,wCAAwC,CAAC,CAAC;IAEzF,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,QAAQ,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,QAAQ,CAAC,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,cAAc,CAAC;gBAAE,MAAM,KAAK,CAAC;YACpD,MAAM,IAAI,CAAC,CAAC;YACZ,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,IAAI,MAAM,GAAG,CAAC;QACZ,MAAM,IAAI,cAAc,CACtB,kBAAkB,EAClB,GAAG,MAAM,OAAO,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,mBAAmB,CACzE,CAAC;AACN,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GraphDoc, RenderManifest } from "@coldtea/pr-lens-schema";
|
|
2
|
+
/**
|
|
3
|
+
* How the comment is recognised on a second run, so a pull request keeps one
|
|
4
|
+
* PR Lens comment instead of collecting one per push. Nothing else may spell
|
|
5
|
+
* this string: a marker that drifts orphans every comment already posted.
|
|
6
|
+
*/
|
|
7
|
+
export declare const COMMENT_MARKER = "<!-- pr-lens -->";
|
|
8
|
+
export type CommentOptions = {
|
|
9
|
+
graph: GraphDoc;
|
|
10
|
+
manifest: RenderManifest;
|
|
11
|
+
/** Prefix for assets the manifest records as local paths, e.g. a raw content URL. */
|
|
12
|
+
assetBaseUrl: string | undefined;
|
|
13
|
+
branding: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare const composeComment: (options: CommentOptions) => string;
|
|
16
|
+
//# sourceMappingURL=comment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../src/comment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAqB,cAAc,EAAQ,MAAM,yBAAyB,CAAC;AAGjG;;;;GAIG;AACH,eAAO,MAAM,cAAc,qBAAqB,CAAC;AAKjD,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;IACzB,qFAAqF;IACrF,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AA8IF,eAAO,MAAM,cAAc,YAAa,cAAc,KAAG,MA2BxD,CAAC"}
|
package/dist/comment.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { assertNever } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { PrLensCliError } from "./errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* How the comment is recognised on a second run, so a pull request keeps one
|
|
5
|
+
* PR Lens comment instead of collecting one per push. Nothing else may spell
|
|
6
|
+
* this string: a marker that drifts orphans every comment already posted.
|
|
7
|
+
*/
|
|
8
|
+
export const COMMENT_MARKER = "<!-- pr-lens -->";
|
|
9
|
+
const PROJECT_URL = "https://github.com/coldteadotai/pr-lens";
|
|
10
|
+
const COLDTEA_URL = "https://coldtea.ai";
|
|
11
|
+
const escape = (value) => value
|
|
12
|
+
.replaceAll("&", "&")
|
|
13
|
+
.replaceAll("<", "<")
|
|
14
|
+
.replaceAll(">", ">")
|
|
15
|
+
.replaceAll('"', """);
|
|
16
|
+
/**
|
|
17
|
+
* Model-authored prose, rendered as the words it is.
|
|
18
|
+
*
|
|
19
|
+
* Every string in the document was written by a model reading a diff, and a
|
|
20
|
+
* pull request can carry whatever text an author likes into that diff. So
|
|
21
|
+
* none of it may reach GitHub as markup: escaping the HTML is only half the
|
|
22
|
+
* job, because markdown would still turn `[Security update](http://…)` into a
|
|
23
|
+
* link that looks like ours. Each string therefore lands inside an HTML
|
|
24
|
+
* element — inside one, markdown is not parsed at all — on a single line, so
|
|
25
|
+
* a blank line cannot end the block and let the rest through.
|
|
26
|
+
*
|
|
27
|
+
* The zero-width space after an @ or a # is the last piece: those are matched
|
|
28
|
+
* after markdown, on the rendered text, and would otherwise notify a person
|
|
29
|
+
* or cross-link an issue on the say-so of a diff.
|
|
30
|
+
*/
|
|
31
|
+
const text = (value) => escape(value.replace(/\s+/g, " ").trim()).replace(/([@#])(?=[\w-])/g, "$1​");
|
|
32
|
+
const href = (asset, assetBaseUrl) => {
|
|
33
|
+
if (asset.url !== undefined)
|
|
34
|
+
return asset.url;
|
|
35
|
+
if (asset.path === undefined || assetBaseUrl === undefined)
|
|
36
|
+
throw new PrLensCliError("USAGE", `asset '${asset.id}' is a local file, so the comment has nowhere to point`, "pass --asset-base-url with the location the rendered SVGs are published at");
|
|
37
|
+
return `${assetBaseUrl.replace(/\/+$/, "")}/${asset.path.replace(/^\/+/, "")}`;
|
|
38
|
+
};
|
|
39
|
+
const pairsByLens = (assets) => {
|
|
40
|
+
const pairs = new Map();
|
|
41
|
+
for (const asset of assets) {
|
|
42
|
+
const pair = pairs.get(asset.lens) ?? { light: undefined, dark: undefined };
|
|
43
|
+
pairs.set(asset.lens, { ...pair, [asset.theme]: asset });
|
|
44
|
+
}
|
|
45
|
+
return pairs;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* A `<picture>` is what makes one comment readable in both GitHub themes: the
|
|
49
|
+
* dark source is swapped in by the browser, with the light asset as the `img`
|
|
50
|
+
* every other reader — email, mobile, an old client — falls back to.
|
|
51
|
+
*
|
|
52
|
+
* The whole thing is a link to the image itself, because a comment column is
|
|
53
|
+
* about 830 pixels wide and a diagram of a system with several lanes is
|
|
54
|
+
* several times that. It arrives scaled to fit, which is right for scanning,
|
|
55
|
+
* and one click gives a reader the size the labels were drawn at.
|
|
56
|
+
*/
|
|
57
|
+
const picture = (pair, alt, assetBaseUrl) => {
|
|
58
|
+
const fallback = pair.light ?? pair.dark;
|
|
59
|
+
if (fallback === undefined)
|
|
60
|
+
return "";
|
|
61
|
+
const source = escape(href(fallback, assetBaseUrl));
|
|
62
|
+
const image = `<img alt="${text(alt)}" src="${source}" width="${fallback.width}">`;
|
|
63
|
+
const shown = pair.dark === undefined || pair.light === undefined
|
|
64
|
+
? image
|
|
65
|
+
: [
|
|
66
|
+
"<picture>",
|
|
67
|
+
` <source media="(prefers-color-scheme: dark)" srcset="${escape(href(pair.dark, assetBaseUrl))}">`,
|
|
68
|
+
` ${image}`,
|
|
69
|
+
"</picture>",
|
|
70
|
+
].join("\n");
|
|
71
|
+
return `<a href="${source}">${shown}</a>`;
|
|
72
|
+
};
|
|
73
|
+
const lensLabel = (lens) => {
|
|
74
|
+
switch (lens) {
|
|
75
|
+
case "architecture":
|
|
76
|
+
return "Architecture";
|
|
77
|
+
case "data-flow":
|
|
78
|
+
return "Data flow";
|
|
79
|
+
default:
|
|
80
|
+
return assertNever(lens, "Unhandled lens");
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const statsLine = (graph) => {
|
|
84
|
+
const { stats } = graph;
|
|
85
|
+
if (stats === undefined)
|
|
86
|
+
return "";
|
|
87
|
+
const chips = [
|
|
88
|
+
stats.filesChanged === undefined
|
|
89
|
+
? undefined
|
|
90
|
+
: `${stats.filesChanged} ${stats.filesChanged === 1 ? "file" : "files"}`,
|
|
91
|
+
stats.additions === undefined ? undefined : `+${stats.additions}`,
|
|
92
|
+
stats.deletions === undefined ? undefined : `−${stats.deletions}`,
|
|
93
|
+
...stats.chips.map((chip) => `${chip.label} ${chip.value}`),
|
|
94
|
+
].filter((chip) => chip !== undefined);
|
|
95
|
+
return `<p>${chips.map((chip) => `<code>${text(chip)}</code>`).join(" · ")}</p>`;
|
|
96
|
+
};
|
|
97
|
+
const viewSection = (view, assets, assetBaseUrl) => {
|
|
98
|
+
const own = assets.get(view.id) ?? [];
|
|
99
|
+
const pair = pairsByLens(own).get(view.lens);
|
|
100
|
+
const body = [
|
|
101
|
+
view.summary === undefined ? "" : `<p>${text(view.summary)}</p>`,
|
|
102
|
+
pair === undefined ? "" : picture(pair, view.title, assetBaseUrl),
|
|
103
|
+
...view.children.map((child) => viewSection(child, assets, assetBaseUrl)),
|
|
104
|
+
].filter((part) => part !== "");
|
|
105
|
+
return [
|
|
106
|
+
`<details${view.defaultOpen ? " open" : ""}>`,
|
|
107
|
+
`<summary><b>${text(view.title)}</b></summary>`,
|
|
108
|
+
"",
|
|
109
|
+
...body.flatMap((part) => [part, ""]),
|
|
110
|
+
"</details>",
|
|
111
|
+
].join("\n");
|
|
112
|
+
};
|
|
113
|
+
/** Assets keyed by the view they belong to; the root render has no view. */
|
|
114
|
+
const ROOT = "";
|
|
115
|
+
const byView = (manifest) => {
|
|
116
|
+
const grouped = new Map();
|
|
117
|
+
for (const asset of manifest.assets) {
|
|
118
|
+
const key = asset.view ?? ROOT;
|
|
119
|
+
grouped.set(key, [...(grouped.get(key) ?? []), asset]);
|
|
120
|
+
}
|
|
121
|
+
return grouped;
|
|
122
|
+
};
|
|
123
|
+
export const composeComment = (options) => {
|
|
124
|
+
const { graph, manifest, assetBaseUrl, branding } = options;
|
|
125
|
+
const assets = byView(manifest);
|
|
126
|
+
const roots = pairsByLens(assets.get(ROOT) ?? []);
|
|
127
|
+
const diagrams = graph.lenses.flatMap((lens) => {
|
|
128
|
+
const pair = roots.get(lens);
|
|
129
|
+
return pair === undefined ? [] : [picture(pair, `${graph.title} — ${lensLabel(lens)}`, assetBaseUrl)];
|
|
130
|
+
});
|
|
131
|
+
const footer = branding
|
|
132
|
+
? `<sub>◈ Rendered by <a href="${PROJECT_URL}">PR Lens</a> · from the team behind <a href="${COLDTEA_URL}">Coldtea</a></sub>`
|
|
133
|
+
: "";
|
|
134
|
+
return [
|
|
135
|
+
COMMENT_MARKER,
|
|
136
|
+
`<h3>${text(graph.title)}</h3>`,
|
|
137
|
+
graph.summary === undefined ? "" : `<p>${text(graph.summary)}</p>`,
|
|
138
|
+
statsLine(graph),
|
|
139
|
+
...diagrams,
|
|
140
|
+
...graph.views.map((view) => viewSection(view, assets, assetBaseUrl)),
|
|
141
|
+
footer === "" ? "" : "---",
|
|
142
|
+
footer,
|
|
143
|
+
]
|
|
144
|
+
.filter((block) => block !== "")
|
|
145
|
+
.join("\n\n")
|
|
146
|
+
.concat("\n");
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=comment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.js","sourceRoot":"","sources":["../src/comment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Config } from "@coldtea/pr-lens-schema";
|
|
2
|
+
/** Where a repository puts its corrections, in the order they are looked for. */
|
|
3
|
+
export declare const CONFIG_PATHS: readonly [".github/pr-lens.yml", ".github/pr-lens.yaml"];
|
|
4
|
+
export type LoadedConfig = {
|
|
5
|
+
path: string;
|
|
6
|
+
config: Config;
|
|
7
|
+
};
|
|
8
|
+
export declare const loadConfig: (path: string) => Promise<LoadedConfig>;
|
|
9
|
+
export declare const discoverConfig: (root: string) => Promise<LoadedConfig | undefined>;
|
|
10
|
+
//# sourceMappingURL=config-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../src/config-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAKtD,iFAAiF;AACjF,eAAO,MAAM,YAAY,YAAI,qBAAqB,EAAE,sBAAsB,CAAU,CAAC;AAErF,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,eAAO,MAAM,UAAU,SAAgB,MAAM,KAAG,OAAO,CAAC,YAAY,CAGlE,CAAC;AAEH,eAAO,MAAM,cAAc,SAAgB,MAAM,KAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAUnF,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readConfig } from "./document.js";
|
|
4
|
+
/** Where a repository puts its corrections, in the order they are looked for. */
|
|
5
|
+
export const CONFIG_PATHS = [".github/pr-lens.yml", ".github/pr-lens.yaml"];
|
|
6
|
+
export const loadConfig = async (path) => ({
|
|
7
|
+
path,
|
|
8
|
+
config: await readConfig(path),
|
|
9
|
+
});
|
|
10
|
+
export const discoverConfig = async (root) => {
|
|
11
|
+
for (const candidate of CONFIG_PATHS) {
|
|
12
|
+
const path = join(root, candidate);
|
|
13
|
+
const found = await access(path).then(() => true, () => false);
|
|
14
|
+
if (found)
|
|
15
|
+
return loadConfig(path);
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=config-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-file.js","sourceRoot":"","sources":["../src/config-file.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,iFAAiF;AACjF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,qBAAqB,EAAE,sBAAsB,CAAU,CAAC;AAIrF,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,IAAY,EAAyB,EAAE,CAAC,CAAC;IACxE,IAAI;IACJ,MAAM,EAAE,MAAM,UAAU,CAAC,IAAI,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,IAAY,EAAqC,EAAE;IACtF,KAAK,MAAM,SAAS,IAAI,YAAY,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CACnC,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,KAAK,CACZ,CAAC;QACF,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Config, GraphDoc } from "@coldtea/pr-lens-schema";
|
|
2
|
+
/**
|
|
3
|
+
* Which of a repository's corrections match no node in this document.
|
|
4
|
+
*
|
|
5
|
+
* A selector that matches nothing is a config that has drifted since it was
|
|
6
|
+
* written — the file it named was moved or deleted — and saying so is the
|
|
7
|
+
* difference between a correction that stopped working and one that never
|
|
8
|
+
* did. It is never a reason to delete a correction that does match: that one
|
|
9
|
+
* is what keeps holding as the code moves.
|
|
10
|
+
*
|
|
11
|
+
* Which nodes a selector matches is the renderer's answer to give, so it is
|
|
12
|
+
* asked rather than recomputed. A second opinion about what a selector means
|
|
13
|
+
* is how the two drift apart.
|
|
14
|
+
*/
|
|
15
|
+
export declare const unmatchedCorrections: (graph: GraphDoc, config: Config) => string[];
|
|
16
|
+
//# sourceMappingURL=corrections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corrections.d.ts","sourceRoot":"","sources":["../src/corrections.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AAyDhF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,UAAW,QAAQ,UAAU,MAAM,KAAG,MAAM,EAc5E,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { applyCorrections, graphContentHash } from "@coldtea/pr-lens-renderer";
|
|
2
|
+
const NOTHING = { rename: [], exclude: [], lane: [], group: [] };
|
|
3
|
+
/**
|
|
4
|
+
* A label, lane id and group id that appear nowhere in this document.
|
|
5
|
+
*
|
|
6
|
+
* It is what makes the question answerable at all: a rename to the label a
|
|
7
|
+
* node already carries moves nothing, and so does a lane pin into the lane a
|
|
8
|
+
* node is already in. Asking with a value the document cannot already hold
|
|
9
|
+
* separates "no node matches this" from "every node it matches already agrees
|
|
10
|
+
* with it" — and the difference matters, because the second is a live
|
|
11
|
+
* correction holding the line against the next inference.
|
|
12
|
+
*/
|
|
13
|
+
const unusedEverywhere = (graph) => {
|
|
14
|
+
const taken = new Set([
|
|
15
|
+
...graph.nodes.map((node) => node.label),
|
|
16
|
+
...graph.nodes.flatMap((node) => (node.group === undefined ? [] : [node.group])),
|
|
17
|
+
...graph.nodes.map((node) => node.lane),
|
|
18
|
+
...graph.lanes.map((lane) => lane.id),
|
|
19
|
+
]);
|
|
20
|
+
let candidate = "pr-lens-probe";
|
|
21
|
+
for (let suffix = 1; taken.has(candidate); suffix += 1)
|
|
22
|
+
candidate = `pr-lens-probe-${suffix}`;
|
|
23
|
+
return candidate;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Each correction on its own, aimed somewhere the document is not, so that
|
|
27
|
+
* matching it always shows. Excluding is the one that needs no probe: a node
|
|
28
|
+
* a rule matches is always removed.
|
|
29
|
+
*/
|
|
30
|
+
const probes = (map, probe) => [
|
|
31
|
+
...map.rename.map((rule) => ({
|
|
32
|
+
what: "rename",
|
|
33
|
+
match: rule.match,
|
|
34
|
+
only: { ...NOTHING, rename: [{ match: rule.match, to: probe }] },
|
|
35
|
+
})),
|
|
36
|
+
...map.exclude.map((selector) => ({
|
|
37
|
+
what: "exclude",
|
|
38
|
+
match: selector,
|
|
39
|
+
only: { ...NOTHING, exclude: [selector] },
|
|
40
|
+
})),
|
|
41
|
+
...map.lane.map((rule) => ({
|
|
42
|
+
what: "lane",
|
|
43
|
+
match: rule.match,
|
|
44
|
+
only: { ...NOTHING, lane: [{ match: rule.match, lane: probe }] },
|
|
45
|
+
})),
|
|
46
|
+
...map.group.map((rule) => ({
|
|
47
|
+
what: "group",
|
|
48
|
+
match: rule.match,
|
|
49
|
+
only: { ...NOTHING, group: [{ match: rule.match, group: probe }] },
|
|
50
|
+
})),
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Which of a repository's corrections match no node in this document.
|
|
54
|
+
*
|
|
55
|
+
* A selector that matches nothing is a config that has drifted since it was
|
|
56
|
+
* written — the file it named was moved or deleted — and saying so is the
|
|
57
|
+
* difference between a correction that stopped working and one that never
|
|
58
|
+
* did. It is never a reason to delete a correction that does match: that one
|
|
59
|
+
* is what keeps holding as the code moves.
|
|
60
|
+
*
|
|
61
|
+
* Which nodes a selector matches is the renderer's answer to give, so it is
|
|
62
|
+
* asked rather than recomputed. A second opinion about what a selector means
|
|
63
|
+
* is how the two drift apart.
|
|
64
|
+
*/
|
|
65
|
+
export const unmatchedCorrections = (graph, config) => {
|
|
66
|
+
const before = graphContentHash(graph);
|
|
67
|
+
const probe = unusedEverywhere(graph);
|
|
68
|
+
return probes(config.map, probe).flatMap(({ what, match, only }) => {
|
|
69
|
+
try {
|
|
70
|
+
if (graphContentHash(applyCorrections(graph, only)) !== before)
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// A correction that emptied the document is a correction that matched.
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return [`${what} '${match}' changed nothing — no node in this document matches it`];
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=corrections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corrections.js","sourceRoot":"","sources":["../src/corrections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Config, type GraphDoc, type PatchDoc, type PrLensSchemaError, type RenderManifest } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { PrLensCliError } from "./errors.js";
|
|
3
|
+
export type DocumentKind = "graph" | "patch" | "render-manifest" | "config";
|
|
4
|
+
export type ValidatedDocument = {
|
|
5
|
+
kind: "graph";
|
|
6
|
+
document: GraphDoc;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "patch";
|
|
9
|
+
document: PatchDoc;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "render-manifest";
|
|
12
|
+
document: RenderManifest;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "config";
|
|
15
|
+
document: Config;
|
|
16
|
+
};
|
|
17
|
+
export declare const describeDocumentKind: (kind: DocumentKind) => string;
|
|
18
|
+
export declare const invalidDocument: (path: string, kind: DocumentKind, error: PrLensSchemaError) => PrLensCliError;
|
|
19
|
+
export declare const readGraphDoc: (path: string) => Promise<GraphDoc>;
|
|
20
|
+
export declare const readRenderManifest: (path: string) => Promise<RenderManifest>;
|
|
21
|
+
export declare const readConfig: (path: string) => Promise<Config>;
|
|
22
|
+
export declare const validateDocumentFile: (path: string) => Promise<ValidatedDocument>;
|
|
23
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,MAAM,EACX,KAAK,QAAQ,EAEb,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,eAAO,MAAM,oBAAoB,SAAU,YAAY,KAAG,MAazD,CAAC;AAEF,eAAO,MAAM,eAAe,SACpB,MAAM,QACN,YAAY,SACX,iBAAiB,KACvB,cAKA,CAAC;AAsBJ,eAAO,MAAM,YAAY,SAAgB,MAAM,KAAG,OAAO,CAAC,QAAQ,CACM,CAAC;AAEzE,eAAO,MAAM,kBAAkB,SAAgB,MAAM,KAAG,OAAO,CAAC,cAAc,CACU,CAAC;AAEzF,eAAO,MAAM,UAAU,SAAgB,MAAM,KAAG,OAAO,CAAC,MAAM,CACS,CAAC;AAsBxE,eAAO,MAAM,oBAAoB,SAAgB,MAAM,KAAG,OAAO,CAAC,iBAAiB,CAgBlF,CAAC"}
|