@scientific-method/standard-checker 0.3.0 → 0.4.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/README.md +13 -4
- package/dist/checks/kaitai.d.ts +2 -1
- package/dist/checks/kaitai.js +47 -34
- package/dist/context.d.ts +3 -1
- package/dist/options.d.ts +2 -0
- package/dist/options.js +7 -1
- package/dist/problems.d.ts +22 -3
- package/dist/problems.js +14 -3
- package/dist/standard-checker.js +13 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,13 @@ pnpm exec standard-checker --check # check, and fail on a stale index or PARI
|
|
|
13
13
|
|
|
14
14
|
It exits with 0 when the repository passes, 1 when it reports problems (one line per problem,
|
|
15
15
|
starting with the file's path), and 2 when the options are invalid. `--record-validation` cannot be
|
|
16
|
-
combined with `--check`.
|
|
16
|
+
combined with `--check`, nor `--require-ksc` with `--no-ksy`.
|
|
17
|
+
|
|
18
|
+
A pass ends with `spec check passed:` and the counts of entries, parity rows and deviations. When a
|
|
19
|
+
step of the check did not run, it ends with `spec check passed with skipped steps:`, the counts, and
|
|
20
|
+
`Skipped:` followed by each step and why, such as
|
|
21
|
+
`Skipped: Kaitai compilation of 2 definitions (--no-ksy).` A failing run lists the skipped steps
|
|
22
|
+
after its problems.
|
|
17
23
|
|
|
18
24
|
A problem that breaks a numbered rule of the standard ends with the rule's label in brackets, such
|
|
19
25
|
as `[STATUS-14]`. The standard opens that rule with the heading `###### STATUS-14`, anchored at
|
|
@@ -28,7 +34,8 @@ problems under other sections carry no label yet.
|
|
|
28
34
|
| `--root <dir>` | The repository to check. | the current directory |
|
|
29
35
|
| `--check` | Fail when an index or `PARITY.md` is stale, instead of rewriting it. | rewrite |
|
|
30
36
|
| `--base <ref>` | Also fail when a spec ID, area or deviation that exists at `<ref>` is gone. | where HEAD forked from `origin/$GITHUB_BASE_REF` or `origin/main`, when that resolves |
|
|
31
|
-
| `--no-ksy` | Skip compiling the Kaitai definitions in `spec/formats/`. | compile |
|
|
37
|
+
| `--no-ksy` | Skip compiling the Kaitai definitions in `spec/formats/`. The result line names the skipped compilation. | compile |
|
|
38
|
+
| `--require-ksc` | Fail when `spec/formats/` holds Kaitai definitions and no compiler is found, instead of passing with the compilation skipped. | pass with the compilation skipped |
|
|
32
39
|
| `--glossary <path>` | Also accept the terms of a draft glossary file, or of a directory of them. | none |
|
|
33
40
|
| `--code <dirs>` | Comma-separated directories whose files may cite spec and deviation IDs and hold `PLACEHOLDER` comments. | `src,tests,tools` |
|
|
34
41
|
| `--references <dirs>` | Comma-separated directories whose files may cite IDs but whose `PLACEHOLDER` comments do not count against parity. | none |
|
|
@@ -39,12 +46,14 @@ problems under other sections carry no label yet.
|
|
|
39
46
|
| `--help` | Print the options. | |
|
|
40
47
|
|
|
41
48
|
The `KSC` environment variable names the Kaitai Struct compiler. Without it, the checker looks for
|
|
42
|
-
`kaitai-struct-compiler` or `ksc` on `PATH
|
|
49
|
+
`kaitai-struct-compiler` or `ksc` on `PATH`. When it finds neither, it skips the compilation and
|
|
50
|
+
names it in the result line, or with `--require-ksc` reports the missing compiler as a problem.
|
|
43
51
|
|
|
44
52
|
## In GitHub Actions
|
|
45
53
|
|
|
46
54
|
The toolkit's `actions/check-documentation` composite action runs this checker with `--check`,
|
|
47
|
-
installs a pinned Kaitai compiler when the repository has `.ksy` files
|
|
55
|
+
installs a pinned Kaitai compiler when the repository has `.ksy` files (and then passes
|
|
56
|
+
`--require-ksc`), and exposes every option
|
|
48
57
|
above as an input. Pin the action to the toolkit commit whose `packages/standard-checker` matches
|
|
49
58
|
the version installed here, so CI and local runs apply the same checks.
|
|
50
59
|
|
package/dist/checks/kaitai.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Context } from "../context.ts";
|
|
2
2
|
/**
|
|
3
3
|
* Checks that each .ksy file in spec/formats/ belongs to a format entry, and compiles them all with
|
|
4
|
-
* the Kaitai Struct compiler,
|
|
4
|
+
* the Kaitai Struct compiler. With --no-ksy, or with no compiler found, the compilation is recorded
|
|
5
|
+
* as a skipped step; with --require-ksc, a missing compiler is a problem instead.
|
|
5
6
|
*/
|
|
6
7
|
export declare function compileKaitai(ctx: Context): void;
|
package/dist/checks/kaitai.js
CHANGED
|
@@ -5,47 +5,60 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import { basename, join } from "node:path";
|
|
6
6
|
/**
|
|
7
7
|
* Checks that each .ksy file in spec/formats/ belongs to a format entry, and compiles them all with
|
|
8
|
-
* the Kaitai Struct compiler,
|
|
8
|
+
* the Kaitai Struct compiler. With --no-ksy, or with no compiler found, the compilation is recorded
|
|
9
|
+
* as a skipped step; with --require-ksc, a missing compiler is a problem instead.
|
|
9
10
|
*/
|
|
10
11
|
export function compileKaitai(ctx) {
|
|
11
|
-
const { problem } = ctx;
|
|
12
|
+
const { problem, skip } = ctx;
|
|
12
13
|
const { entries } = ctx.spec;
|
|
13
|
-
const { specDir, skipKsy } = ctx.config;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
const { specDir, skipKsy, requireKsc } = ctx.config;
|
|
15
|
+
const ksys = [];
|
|
16
|
+
const fd = join(specDir, "formats");
|
|
17
|
+
if (existsSync(fd))
|
|
18
|
+
for (const f of readdirSync(fd))
|
|
19
|
+
if (f.endsWith(".ksy"))
|
|
20
|
+
ksys.push(join(fd, f));
|
|
21
|
+
for (const k of ksys) {
|
|
22
|
+
const id = basename(k, ".ksy")
|
|
23
|
+
.toUpperCase()
|
|
24
|
+
.replace(/^FMT_([A-Z0-9]+)_(\d+)$/, "FMT-$1-$2");
|
|
25
|
+
if (!entries.has(id))
|
|
26
|
+
problem(k, `belongs to no format entry (${id})`);
|
|
27
|
+
}
|
|
28
|
+
if (!ksys.length)
|
|
29
|
+
return;
|
|
30
|
+
const definitions = `${ksys.length} definition${ksys.length === 1 ? "" : "s"}`;
|
|
31
|
+
if (skipKsy) {
|
|
32
|
+
skip(`Kaitai compilation of ${definitions} (--no-ksy)`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const compiler = findKaitai();
|
|
36
|
+
if (!compiler) {
|
|
37
|
+
const missing = "no Kaitai Struct compiler found, set KSC or install kaitai-struct-compiler";
|
|
38
|
+
if (requireKsc)
|
|
39
|
+
problem(null, `${missing}. --require-ksc requires compiling the ${definitions} in spec/formats/`);
|
|
40
|
+
else
|
|
41
|
+
skip(`Kaitai compilation of ${definitions} (${missing})`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const out = mkdtempSync(join(tmpdir(), "ksy-check-"));
|
|
45
|
+
const fixed = [...compiler.args, "--target", "python", "--outdir", out, "--import-path", fd];
|
|
46
|
+
try {
|
|
47
|
+
for (const batch of kaitaiBatches([compiler.cmd, ...fixed], ksys)) {
|
|
32
48
|
try {
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
runTool(compiler.cmd, [...fixed, ...batch]);
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
const failure = err;
|
|
39
|
-
problem(null, `Kaitai definitions do not compile:\n${String(failure.stdout ?? "")}${String(failure.stderr ?? "")}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
49
|
+
runTool(compiler.cmd, [...fixed, ...batch]);
|
|
42
50
|
}
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
catch (err) {
|
|
52
|
+
// A compiler that cannot be started, such as a KSC naming a missing file, prints nothing,
|
|
53
|
+
// so its error message is the only account of the failure.
|
|
54
|
+
const failure = err;
|
|
55
|
+
const output = `${String(failure.stdout ?? "")}${String(failure.stderr ?? "")}`;
|
|
56
|
+
problem(null, `Kaitai definitions do not compile:\n${output || (failure.message ?? String(err))}`);
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
rmSync(out, { recursive: true, force: true });
|
|
49
62
|
}
|
|
50
63
|
}
|
|
51
64
|
// cmd.exe takes a command line of at most 8,191 characters, and the compiler's .bat launcher adds
|
package/dist/context.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Config } from "./options.ts";
|
|
2
|
-
import type { Problem } from "./problems.ts";
|
|
2
|
+
import type { Problem, Skip } from "./problems.ts";
|
|
3
3
|
import type { CodeFile, CodeRange, Entry, Meta } from "./types.ts";
|
|
4
4
|
/** The spec as the load phase read it. Later phases add to entries (Entry.code, Entry.valueTables). */
|
|
5
5
|
export interface Spec {
|
|
@@ -27,6 +27,8 @@ export interface LoadContext {
|
|
|
27
27
|
}
|
|
28
28
|
/** What every phase after the load needs. */
|
|
29
29
|
export interface Context extends LoadContext {
|
|
30
|
+
/** Records a step of the check that did not run. */
|
|
31
|
+
skip: Skip;
|
|
30
32
|
/** The spec as loaded. */
|
|
31
33
|
spec: Spec;
|
|
32
34
|
/** The files of the --code and --references directories, read on first use. */
|
package/dist/options.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface Config {
|
|
|
8
8
|
checkOnly: boolean;
|
|
9
9
|
/** --no-ksy: skip compiling the Kaitai definitions. */
|
|
10
10
|
skipKsy: boolean;
|
|
11
|
+
/** --require-ksc: fail when there are Kaitai definitions and no compiler is found. */
|
|
12
|
+
requireKsc: boolean;
|
|
11
13
|
/** --base, or null when it was not given. */
|
|
12
14
|
baseArg: string | null;
|
|
13
15
|
/** Every --glossary path, in order. */
|
package/dist/options.js
CHANGED
|
@@ -10,7 +10,7 @@ export const dirList = (value, fallback) => value === undefined
|
|
|
10
10
|
.split(",")
|
|
11
11
|
.map((x) => x.trim())
|
|
12
12
|
.filter(Boolean);
|
|
13
|
-
const FLAGS = ["--check", "--no-ksy"];
|
|
13
|
+
const FLAGS = ["--check", "--no-ksy", "--require-ksc"];
|
|
14
14
|
const VALUED = [
|
|
15
15
|
"--root",
|
|
16
16
|
"--base",
|
|
@@ -52,6 +52,11 @@ export function parseOptions(argv) {
|
|
|
52
52
|
process.exit(2);
|
|
53
53
|
}
|
|
54
54
|
const skipKsy = options["no-ksy"] === true;
|
|
55
|
+
const requireKsc = options["require-ksc"] === true;
|
|
56
|
+
if (skipKsy && requireKsc) {
|
|
57
|
+
console.error("--require-ksc requires the Kaitai compilation that --no-ksy skips, so they cannot be combined");
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
55
60
|
const baseArg = options.base ?? null;
|
|
56
61
|
const codeRoots = dirList(options.code, ["src", "tests", "tools"]);
|
|
57
62
|
const referenceRoots = dirList(options.references, []);
|
|
@@ -75,6 +80,7 @@ export function parseOptions(argv) {
|
|
|
75
80
|
specDir,
|
|
76
81
|
checkOnly,
|
|
77
82
|
skipKsy,
|
|
83
|
+
requireKsc,
|
|
78
84
|
baseArg,
|
|
79
85
|
glossaryDrafts: options.glossary,
|
|
80
86
|
codeRoots,
|
package/dist/problems.d.ts
CHANGED
|
@@ -12,15 +12,34 @@ export type Rule = `IDENTIFIERS-${UpTo<7>}` | `STATUS-${UpTo<41>}` | `ENTRY-TYPE
|
|
|
12
12
|
* a numbered rule names it.
|
|
13
13
|
*/
|
|
14
14
|
export type Problem = (file: string | null, message: string, rule?: Rule) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Records a step of the check that did not run, with the reason, such as "Kaitai compilation of 2
|
|
17
|
+
* definitions (--no-ksy)". The result line names every skipped step, so a pass never reads as a
|
|
18
|
+
* full check when part of it did not run. The line joins the steps with "; ", so a step's text
|
|
19
|
+
* must not contain "; ".
|
|
20
|
+
*/
|
|
21
|
+
export type Skip = (step: string) => void;
|
|
22
|
+
/** What a run found, for the result line. */
|
|
23
|
+
export interface Counts {
|
|
24
|
+
/** Spec entries. */
|
|
25
|
+
entries: number;
|
|
26
|
+
/** PARITY.md rows. */
|
|
27
|
+
parityRows: number;
|
|
28
|
+
/** Deviations. */
|
|
29
|
+
deviations: number;
|
|
30
|
+
}
|
|
15
31
|
/** The problem collector of one run. */
|
|
16
32
|
export interface Problems {
|
|
17
33
|
/** Records a problem. */
|
|
18
34
|
problem: Problem;
|
|
35
|
+
/** Records a skipped step. */
|
|
36
|
+
skip: Skip;
|
|
19
37
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
38
|
+
* With problems, prints every distinct one in the order found, the summary and the skipped
|
|
39
|
+
* steps, and exits with 1. With none, prints the result line: "spec check passed: " and counts,
|
|
40
|
+
* or "spec check passed with skipped steps: " and counts followed by the skipped steps.
|
|
22
41
|
*/
|
|
23
|
-
report(
|
|
42
|
+
report(counts: Counts): void;
|
|
24
43
|
}
|
|
25
44
|
/** Creates the collector for a run over repoDir, against which problem paths are printed. */
|
|
26
45
|
export declare function createProblems(repoDir: string): Problems;
|
package/dist/problems.js
CHANGED
|
@@ -3,6 +3,7 @@ import { relative } from "node:path";
|
|
|
3
3
|
/** Creates the collector for a run over repoDir, against which problem paths are printed. */
|
|
4
4
|
export function createProblems(repoDir) {
|
|
5
5
|
const problems = [];
|
|
6
|
+
const skipped = [];
|
|
6
7
|
let citedRule = false;
|
|
7
8
|
// A problem that breaks a numbered rule ends with the rule's label, so whoever fixes it can read
|
|
8
9
|
// that one rule instead of the whole section.
|
|
@@ -11,17 +12,27 @@ export function createProblems(repoDir) {
|
|
|
11
12
|
citedRule = true;
|
|
12
13
|
problems.push(`${file ? relative(repoDir, file).replaceAll("\\", "/") : "spec"}: ${message}${rule ? ` [${rule}]` : ""}`);
|
|
13
14
|
};
|
|
14
|
-
const
|
|
15
|
+
const skip = (step) => {
|
|
16
|
+
skipped.push(step);
|
|
17
|
+
};
|
|
18
|
+
const skippedLine = () => `Skipped: ${skipped.join("; ")}.`;
|
|
19
|
+
const report = ({ entries, parityRows, deviations }) => {
|
|
20
|
+
const counts = `${entries} entries, ${parityRows} parity rows, ${deviations} deviations.`;
|
|
15
21
|
// The same problem can be found twice, such as a term that cites one finding in two places.
|
|
16
22
|
const unique = [...new Set(problems)];
|
|
17
23
|
if (unique.length) {
|
|
18
24
|
for (const p of unique)
|
|
19
25
|
console.error(p);
|
|
20
|
-
console.error(`\n${unique.length} problem(s) in ${
|
|
26
|
+
console.error(`\n${unique.length} problem(s) in ${entries} spec entries.`);
|
|
21
27
|
if (citedRule)
|
|
22
28
|
console.error("A label in brackets, such as [STATUS-14], names the rule of the documentation standard that the problem breaks. The standard opens it with the heading ###### STATUS-14, anchored at https://dinorefurb.com/documentation-standard/#status-14 and at #status-14 in a vendored copy.");
|
|
29
|
+
if (skipped.length)
|
|
30
|
+
console.error(skippedLine());
|
|
23
31
|
process.exit(1);
|
|
24
32
|
}
|
|
33
|
+
console.log(skipped.length
|
|
34
|
+
? `spec check passed with skipped steps: ${counts} ${skippedLine()}`
|
|
35
|
+
: `spec check passed: ${counts}`);
|
|
25
36
|
};
|
|
26
|
-
return { problem, report };
|
|
37
|
+
return { problem, skip, report };
|
|
27
38
|
}
|
package/dist/standard-checker.js
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
// --check fail when an index or PARITY.md is stale instead of rewriting it
|
|
11
11
|
// --base <ref> also fail when an ID or area that exists at <ref> is gone (default: where
|
|
12
12
|
// HEAD forked from origin/$GITHUB_BASE_REF or origin/main, when it resolves)
|
|
13
|
-
// --no-ksy skip compiling the Kaitai definitions
|
|
13
|
+
// --no-ksy skip compiling the Kaitai definitions; the result line names the skip
|
|
14
|
+
// --require-ksc fail when spec/formats/ holds Kaitai definitions and no compiler is found,
|
|
15
|
+
// instead of passing with the compilation skipped
|
|
14
16
|
// --glossary <path> also accept the terms of a draft glossary file, or of a directory of them
|
|
15
17
|
// --code <dirs> comma-separated directories whose files may cite spec and deviation IDs
|
|
16
18
|
// and hold PLACEHOLDER comments (default: src,tests,tools)
|
|
@@ -36,7 +38,13 @@
|
|
|
36
38
|
// [STATUS-4] for the rule whose heading is anchored at #status-4.
|
|
37
39
|
//
|
|
38
40
|
// The KSC environment variable names the Kaitai Struct compiler. Without it, the check looks for
|
|
39
|
-
// kaitai-struct-compiler or ksc on PATH,
|
|
41
|
+
// kaitai-struct-compiler or ksc on PATH. When it finds neither, the run skips the compilation and
|
|
42
|
+
// says so in its result line, or fails with --require-ksc.
|
|
43
|
+
//
|
|
44
|
+
// It exits with 0 when the spec passes, 1 when it reports problems, and 2 when the options are
|
|
45
|
+
// invalid. A pass prints "spec check passed: " and the counts, or, when a step did not run,
|
|
46
|
+
// "spec check passed with skipped steps: " and the counts followed by "Skipped: " and each step
|
|
47
|
+
// with its reason.
|
|
40
48
|
//
|
|
41
49
|
// No dependencies. The YAML reader understands the subset the standard's front matter uses:
|
|
42
50
|
// scalars, flow lists, and block lists of flat maps.
|
|
@@ -78,11 +86,11 @@ if (argv.includes("--help") || argv.includes("-h")) {
|
|
|
78
86
|
process.exit(0);
|
|
79
87
|
}
|
|
80
88
|
const config = parseOptions(argv);
|
|
81
|
-
const { problem, report } = createProblems(config.repoDir);
|
|
89
|
+
const { problem, skip, report } = createProblems(config.repoDir);
|
|
82
90
|
const spec = loadSpec({ config, problem });
|
|
83
91
|
// The checker's modules all sit in this file's directory, which holds nothing else, so the code
|
|
84
92
|
// checks leave the whole directory out.
|
|
85
|
-
const ctx = { config, problem, spec, codeFiles: createCodeFiles(config, dirname(selfPath)) };
|
|
93
|
+
const ctx = { config, problem, skip, spec, codeFiles: createCodeFiles(config, dirname(selfPath)) };
|
|
86
94
|
const formatNames = checkEntries(ctx);
|
|
87
95
|
checkRules(ctx, formatNames);
|
|
88
96
|
checkFieldNames(ctx, formatNames);
|
|
@@ -98,5 +106,4 @@ const generated = generateIndexes(ctx);
|
|
|
98
106
|
generateParity(ctx, parity, generated);
|
|
99
107
|
writeGenerated(ctx, generated);
|
|
100
108
|
checkLineLimits(ctx);
|
|
101
|
-
report(spec.entries.size);
|
|
102
|
-
console.log(`spec check passed: ${spec.entries.size} entries, ${parity.rows.size} parity rows, ${deviations.size} deviations.`);
|
|
109
|
+
report({ entries: spec.entries.size, parityRows: parity.rows.size, deviations: deviations.size });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scientific-method/standard-checker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Checks a restoration's spec/, parity/ and deviations/ against version 1 of the dinorefurb documentation standard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|