@scientific-method/standard-checker 0.1.0 → 0.2.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.
Files changed (77) hide show
  1. package/README.md +6 -0
  2. package/dist/checks/base.d.ts +7 -0
  3. package/dist/checks/base.js +72 -0
  4. package/dist/checks/claims.d.ts +4 -0
  5. package/dist/checks/claims.js +83 -0
  6. package/dist/checks/comment-addresses.d.ts +3 -0
  7. package/dist/checks/comment-addresses.js +111 -0
  8. package/dist/checks/cross-entry.d.ts +4 -0
  9. package/dist/checks/cross-entry.js +177 -0
  10. package/dist/checks/deviations.d.ts +12 -0
  11. package/dist/checks/deviations.js +101 -0
  12. package/dist/checks/entries.d.ts +7 -0
  13. package/dist/checks/entries.js +130 -0
  14. package/dist/checks/evidence-entries.d.ts +6 -0
  15. package/dist/checks/evidence-entries.js +167 -0
  16. package/dist/checks/formats.d.ts +13 -0
  17. package/dist/checks/formats.js +194 -0
  18. package/dist/checks/kaitai.d.ts +6 -0
  19. package/dist/checks/kaitai.js +103 -0
  20. package/dist/checks/parity.d.ts +30 -0
  21. package/dist/checks/parity.js +189 -0
  22. package/dist/checks/references.d.ts +4 -0
  23. package/dist/checks/references.js +37 -0
  24. package/dist/checks/rules.d.ts +4 -0
  25. package/dist/checks/rules.js +259 -0
  26. package/dist/checks/screens.d.ts +4 -0
  27. package/dist/checks/screens.js +44 -0
  28. package/dist/checks/validation.d.ts +7 -0
  29. package/dist/checks/validation.js +116 -0
  30. package/dist/code-comments.d.ts +3 -0
  31. package/dist/code-comments.js +150 -0
  32. package/dist/code-files.d.ts +11 -0
  33. package/dist/code-files.js +41 -0
  34. package/dist/context.d.ts +34 -0
  35. package/dist/context.js +2 -0
  36. package/dist/evidence.d.ts +37 -0
  37. package/dist/evidence.js +91 -0
  38. package/dist/files.d.ts +11 -0
  39. package/dist/files.js +35 -0
  40. package/dist/generate/indexes.d.ts +3 -0
  41. package/dist/generate/indexes.js +135 -0
  42. package/dist/generate/layout.d.ts +18 -0
  43. package/dist/generate/layout.js +49 -0
  44. package/dist/generate/parity-md.d.ts +7 -0
  45. package/dist/generate/parity-md.js +44 -0
  46. package/dist/generate/write.d.ts +8 -0
  47. package/dist/generate/write.js +75 -0
  48. package/dist/ids.d.ts +13 -0
  49. package/dist/ids.js +22 -0
  50. package/dist/load/builds.d.ts +9 -0
  51. package/dist/load/builds.js +124 -0
  52. package/dist/load/code-ranges.d.ts +4 -0
  53. package/dist/load/code-ranges.js +79 -0
  54. package/dist/load/entries.d.ts +7 -0
  55. package/dist/load/entries.js +57 -0
  56. package/dist/load/glossary.d.ts +12 -0
  57. package/dist/load/glossary.js +70 -0
  58. package/dist/load/readme.d.ts +3 -0
  59. package/dist/load/readme.js +36 -0
  60. package/dist/load/spec.d.ts +6 -0
  61. package/dist/load/spec.js +25 -0
  62. package/dist/locations.d.ts +19 -0
  63. package/dist/locations.js +57 -0
  64. package/dist/markdown.d.ts +27 -0
  65. package/dist/markdown.js +162 -0
  66. package/dist/options.d.ts +34 -0
  67. package/dist/options.js +87 -0
  68. package/dist/problems.d.ts +27 -0
  69. package/dist/problems.js +27 -0
  70. package/dist/standard-checker.js +51 -3033
  71. package/dist/standard.d.ts +65 -0
  72. package/dist/standard.js +173 -0
  73. package/dist/types.d.ts +91 -0
  74. package/dist/types.js +2 -0
  75. package/dist/yaml.d.ts +6 -0
  76. package/dist/yaml.js +173 -0
  77. package/package.json +1 -1
@@ -0,0 +1,65 @@
1
+ import type { Rule } from "./problems.ts";
2
+ import type { Yaml } from "./types.ts";
3
+ /** The kinds of entry, each with its directory under spec/ and the statuses it takes (none for builds and sources). */
4
+ export declare const KINDS: Record<string, {
5
+ dir: string;
6
+ statuses: "claim" | "evidence" | null;
7
+ }>;
8
+ /** The statuses of a claim: a format, rule, bug or screen. */
9
+ export declare const CLAIM_STATUSES: string[];
10
+ /** The claim statuses that rank, lowest first. */
11
+ export declare const SCALE: string[];
12
+ /** The statuses of evidence: a finding or experiment. */
13
+ export declare const EVIDENCE_STATUSES: string[];
14
+ /** The statuses a row of a format's tables can have. */
15
+ export declare const ROW_STATUSES: string[];
16
+ /** The `##` sections of each kind of entry, in order. */
17
+ export declare const SECTIONS: Record<string, string[]>;
18
+ /** The link fields every claim has. */
19
+ export declare const CLAIM_LINKS: string[];
20
+ /**
21
+ * The rules that make the fields every entry, or every claim, has always present. A field of one
22
+ * kind alone is described under that kind, which has no numbered rules yet.
23
+ */
24
+ export declare const FIELD_RULES: Record<string, Rule>;
25
+ /** The front matter fields each kind of entry requires. */
26
+ export declare const FIELDS: Record<string, {
27
+ required: string[];
28
+ }>;
29
+ /** The kinds each kind of claim may name in related. */
30
+ export declare const RELATED_KINDS: Record<string, string[]>;
31
+ /** The columns of the table in each section of a screen that has one. */
32
+ export declare const SCREEN_TABLES: Record<string, string[]>;
33
+ /** The columns of a binary format's layout table. */
34
+ export declare const BINARY_LAYOUT: string[];
35
+ /** The columns of a text format's layout table. */
36
+ export declare const TEXT_LAYOUT: string[];
37
+ /** The columns of an enumeration table. */
38
+ export declare const ENUM_TABLE: string[];
39
+ /** Matches every spec ID (global). */
40
+ export declare const ID_RE: RegExp;
41
+ /** Matches every deviation ID (global). */
42
+ export declare const DEV_RE: RegExp;
43
+ /** Every Markdown file the standard defines, generated or not, is at most this many lines long. */
44
+ export declare const LINE_LIMIT = 1000;
45
+ /**
46
+ * A list written out in a procedure or a table definition holds at most this many values. A longer
47
+ * one takes them from a value file.
48
+ */
49
+ export declare const LIST_LIMIT = 64;
50
+ /** The location rule of each file format that has one, as the comment above describes. */
51
+ export declare const LOCATIONS: Record<string, {
52
+ address?: RegExp;
53
+ offset?: boolean;
54
+ }>;
55
+ /** The file formats that have a location rule. */
56
+ export declare const FORMATS: string[];
57
+ /** The location rule of a file format, or undefined when Standard v1 has none. */
58
+ export declare const locationRule: (format: Yaml) => {
59
+ address?: RegExp;
60
+ offset?: boolean;
61
+ } | undefined;
62
+ /** The problem text for a file format that has no location rule. */
63
+ export declare const unlistedFormat: (format: Yaml) => string;
64
+ /** Names Windows cannot give a file, whatever the extension, so no glossary term may be one. */
65
+ export declare const RESERVED_NAMES: RegExp;
@@ -0,0 +1,173 @@
1
+ // The kinds, statuses, sections, fields and limits of version 1 of the documentation standard.
2
+ /** The kinds of entry, each with its directory under spec/ and the statuses it takes (none for builds and sources). */
3
+ export const KINDS = {
4
+ BLD: { dir: "builds", statuses: null },
5
+ SRC: { dir: "sources", statuses: null },
6
+ FMT: { dir: "formats", statuses: "claim" },
7
+ RULE: { dir: "rules", statuses: "claim" },
8
+ FND: { dir: "findings", statuses: "evidence" },
9
+ EXP: { dir: "experiments", statuses: "evidence" },
10
+ BUG: { dir: "bugs", statuses: "claim" },
11
+ SCR: { dir: "screens", statuses: "claim" },
12
+ };
13
+ /** The statuses of a claim: a format, rule, bug or screen. */
14
+ export const CLAIM_STATUSES = ["unknown", "sourced", "supported", "established", "disputed", "superseded"];
15
+ /** The claim statuses that rank, lowest first. */
16
+ export const SCALE = ["unknown", "sourced", "supported", "established"];
17
+ /** The statuses of evidence: a finding or experiment. */
18
+ export const EVIDENCE_STATUSES = ["recorded", "reproduced", "superseded"];
19
+ /** The statuses a row of a format's tables can have. */
20
+ export const ROW_STATUSES = ["unknown", "sourced", "supported", "established", "disputed"];
21
+ /** The `##` sections of each kind of entry, in order. */
22
+ export const SECTIONS = {
23
+ BLD: ["Obtaining", "Compared with other builds", "Other files", "Code ranges"],
24
+ SRC: ["Use", "Known errors"],
25
+ FND: ["Observation", "Interpretation", "Alternatives", "How to reproduce"],
26
+ EXP: ["Question", "Setup", "Procedure", "Observations", "Results", "Conclusion"],
27
+ FMT: ["Layout", "Enumerations and flags", "Differences between builds", "Coverage", "Open questions"],
28
+ RULE: [
29
+ "Summary",
30
+ "When it runs",
31
+ "Parameters",
32
+ "Inputs",
33
+ "Procedure",
34
+ "Outputs",
35
+ "Edge cases",
36
+ "What the sources say",
37
+ "Differences between builds",
38
+ "Open questions",
39
+ ],
40
+ BUG: [
41
+ "Symptom",
42
+ "Trigger conditions",
43
+ "Mechanism",
44
+ "Frequency",
45
+ "Player reliance",
46
+ "Fixes elsewhere",
47
+ "Differences between builds",
48
+ "Open questions",
49
+ ],
50
+ SCR: [
51
+ "Drawn elements",
52
+ "Mouse input",
53
+ "Keyboard input",
54
+ "Other input",
55
+ "Sounds",
56
+ "States",
57
+ "Timing",
58
+ "Differences between builds",
59
+ "Open questions",
60
+ ],
61
+ };
62
+ const COMMON = ["id", "title", "status", "builds", "superseded_by"];
63
+ /** The link fields every claim has. */
64
+ export const CLAIM_LINKS = ["evidence", "conflicting", "split_with", "related"];
65
+ /**
66
+ * The rules that make the fields every entry, or every claim, has always present. A field of one
67
+ * kind alone is described under that kind, which has no numbered rules yet.
68
+ */
69
+ export const FIELD_RULES = {
70
+ id: "ENTRY-TYPES-4",
71
+ title: "ENTRY-TYPES-4",
72
+ status: "ENTRY-TYPES-4",
73
+ builds: "ENTRY-TYPES-4",
74
+ superseded_by: "ENTRY-TYPES-4",
75
+ evidence: "ENTRY-TYPES-5",
76
+ conflicting: "ENTRY-TYPES-5",
77
+ split_with: "ENTRY-TYPES-5",
78
+ related: "ENTRY-TYPES-6",
79
+ };
80
+ /** The front matter fields each kind of entry requires. */
81
+ export const FIELDS = {
82
+ BLD: {
83
+ required: [
84
+ "id",
85
+ "title",
86
+ "superseded_by",
87
+ "developer",
88
+ "publisher",
89
+ "publisher_version",
90
+ "distribution",
91
+ "languages",
92
+ "int_width",
93
+ "manifest",
94
+ ],
95
+ },
96
+ SRC: { required: ["id", "title", "superseded_by", "author", "date", "location", "xxh3", "licence"] },
97
+ FND: { required: [...COMMON, "recorded_by", "reproduced_by", "method", "locations", "tool", "environment"] },
98
+ EXP: {
99
+ required: [
100
+ ...COMMON,
101
+ "recorded_by",
102
+ "reproduced_by",
103
+ "environment",
104
+ "starting_state",
105
+ "recording",
106
+ "repetitions",
107
+ "fixture",
108
+ ],
109
+ },
110
+ FMT: { required: [...COMMON, "files", "byte_order", "size", "text", "definition", ...CLAIM_LINKS] },
111
+ RULE: { required: [...COMMON, ...CLAIM_LINKS] },
112
+ BUG: { required: [...COMMON, "impact", "intent", "player_reliance", ...CLAIM_LINKS] },
113
+ SCR: { required: [...COMMON, "resolution", ...CLAIM_LINKS] },
114
+ };
115
+ /** The kinds each kind of claim may name in related. */
116
+ export const RELATED_KINDS = {
117
+ RULE: ["RULE", "FMT", "SCR"],
118
+ FMT: ["RULE"],
119
+ BUG: ["RULE", "FMT", "SCR"],
120
+ SCR: ["RULE", "SCR"],
121
+ };
122
+ /** The columns of the table in each section of a screen that has one. */
123
+ export const SCREEN_TABLES = {
124
+ "Drawn elements": ["Element", "Resource", "Shows", "Position", "Shown when", "Evidence"],
125
+ "Mouse input": ["Region", "Rectangle", "Enabled when", "Effect", "Evidence"],
126
+ "Keyboard input": ["Key", "Enabled when", "Effect", "Evidence"],
127
+ "Other input": ["Device", "Input", "Enabled when", "Effect", "Evidence"],
128
+ Sounds: ["Sound", "Resource", "Played when", "Evidence"],
129
+ States: ["State", "Entered when", "Left when", "Evidence"],
130
+ };
131
+ /** The columns of a binary format's layout table. */
132
+ export const BINARY_LAYOUT = ["Offset", "Size", "Type", "Name", "Meaning", "Status", "Evidence"];
133
+ /** The columns of a text format's layout table. */
134
+ export const TEXT_LAYOUT = ["Key", "Type", "Name", "Meaning", "Status", "Evidence"];
135
+ /** The columns of an enumeration table. */
136
+ export const ENUM_TABLE = ["Value", "Name", "Meaning", "Status", "Evidence"];
137
+ /** Matches every spec ID (global). */
138
+ export const ID_RE = /\b(?:(?:FMT|RULE|FND|EXP|BUG|SCR)-[A-Z][A-Z0-9]*-\d{3,}|(?:BLD|SRC)-[A-Z][A-Z0-9.-]*[A-Z0-9])\b/g;
139
+ /** Matches every deviation ID (global). */
140
+ export const DEV_RE = /\bDEV-[A-Z][A-Z0-9]*-\d{3,}\b/g;
141
+ /** Every Markdown file the standard defines, generated or not, is at most this many lines long. */
142
+ export const LINE_LIMIT = 1000;
143
+ /**
144
+ * A list written out in a procedure or a table definition holds at most this many values. A longer
145
+ * one takes them from a value file.
146
+ */
147
+ export const LIST_LIMIT = 64;
148
+ // How a location in each file format is given, per Standard v1. `address` is the notation of a
149
+ // loaded address; `offset` allows a range of the shipped file. Both are judged by the unpacked
150
+ // format when the file is packed. A format not listed here has no rule: the Standard must first decide and document how it
151
+ // is located, and only then is it added here.
152
+ const SEG = /^[0-9A-F]{4}:[0-9A-F]{4}$/;
153
+ const FLAT32 = /^0x[0-9A-F]{8}$/;
154
+ /** The location rule of each file format that has one, as the comment above describes. */
155
+ export const LOCATIONS = {
156
+ MZ: { address: SEG, offset: true }, // offset only for overlay code outside the load image
157
+ COM: { address: SEG },
158
+ NE: { address: SEG },
159
+ PE: { address: FLAT32 },
160
+ LE: { address: FLAT32 },
161
+ LX: { address: FLAT32 },
162
+ ELF: { address: /^0x(?:[0-9A-F]{8}|[0-9A-F]{16})$/ },
163
+ data: { offset: true },
164
+ cdda: { offset: true },
165
+ };
166
+ /** The file formats that have a location rule. */
167
+ export const FORMATS = Object.keys(LOCATIONS);
168
+ /** The location rule of a file format, or undefined when Standard v1 has none. */
169
+ export const locationRule = (format) => (Object.hasOwn(LOCATIONS, format) ? LOCATIONS[format] : undefined);
170
+ /** The problem text for a file format that has no location rule. */
171
+ export const unlistedFormat = (format) => `format ${format} has no location rule in Standard v1 (known: ${FORMATS.join(", ")}); the Standard must document how it is located before it is used`;
172
+ /** Names Windows cannot give a file, whatever the extension, so no glossary term may be one. */
173
+ export const RESERVED_NAMES = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * A value read from front matter. Front matter comes from YAML and every check validates the fields
3
+ * it reads before relying on them, so a value read from it has whatever type the YAML reader produced.
4
+ */
5
+ export type Yaml = any;
6
+ /** The front matter of an entry, or any other map the YAML reader returns. */
7
+ export type Meta = Record<string, Yaml>;
8
+ /** A `##` section of a Markdown file: its title and the text up to the next `##` heading. */
9
+ export interface Section {
10
+ /** The heading text, trimmed. */
11
+ title: string;
12
+ /** The lines under the heading, joined with `\n`. */
13
+ text: string;
14
+ }
15
+ /** A Markdown table, or a value file read as one. */
16
+ export interface Table {
17
+ /** The `###` heading above the table, or null when there is none. */
18
+ heading: string | null;
19
+ /** The header cells. */
20
+ header: string[];
21
+ /** The body rows, as cells. */
22
+ rows: string[][];
23
+ /** The index of the header line within the section text, for a Markdown table. */
24
+ line?: number;
25
+ /** The value file the table was read from, for a value file. */
26
+ file?: string;
27
+ }
28
+ /** A spec entry: one Markdown file with front matter under one of the kind directories of spec/. */
29
+ export interface Entry {
30
+ /** The absolute path of the entry's file. */
31
+ file: string;
32
+ /** The front matter. */
33
+ meta: Meta;
34
+ /** Everything after the front matter. */
35
+ body: string;
36
+ /** The `##` sections of the body. */
37
+ sections: Section[];
38
+ /** The kind the entry's directory stands for, such as `RULE`. */
39
+ kind: string;
40
+ /** For a rule, the text of the ```text blocks of its Procedure, set by the rule checks. */
41
+ code?: string;
42
+ /** For a format, the enumeration tables it keeps in value files, set by the format checks. */
43
+ valueTables?: Table[];
44
+ }
45
+ /** What the cited evidence of a claim, or of a row of its tables, covers for the first build. */
46
+ export interface Facts {
47
+ /** How many sources it cites. */
48
+ sources: number;
49
+ /** How many static findings that list the first build it cites. */
50
+ staticF: number;
51
+ /** How many dynamic findings and experiments that list the first build it cites. */
52
+ dynamic: number;
53
+ /** Whether a complete reading covers it. */
54
+ completeReading: boolean;
55
+ }
56
+ /** A file of a --code or --references directory, read once. */
57
+ export interface CodeFile {
58
+ /** True for a --code directory, false for a --references one. */
59
+ code: boolean;
60
+ /** The absolute path. */
61
+ file: string;
62
+ /** The file's text. */
63
+ text: string;
64
+ }
65
+ /** One piece of comment on a line of source. */
66
+ export interface Comment {
67
+ /** The column the piece starts at. */
68
+ column: number;
69
+ /** The comment text, delimiters included. */
70
+ text: string;
71
+ /** True for the second and later lines of a block comment. */
72
+ continued: boolean;
73
+ }
74
+ /** One line of a source file as the comment reader sees it. */
75
+ export interface CodeLine {
76
+ /** Whether the line holds anything other than comments and whitespace. */
77
+ code: boolean;
78
+ /** The pieces of comment on the line, in order. */
79
+ comments: Comment[];
80
+ }
81
+ /** Renders the text of one generated file at path (relative to the generated tree, without .md) for ids. */
82
+ export type Render = (ids: string[], path: string) => string;
83
+ /** A half-open range of a build file that holds code located by offset, from the build's Code ranges section. */
84
+ export interface CodeRange {
85
+ /** The path of the file in the build's manifest. */
86
+ file: string;
87
+ /** The first offset in the range. */
88
+ start: bigint;
89
+ /** One past the last offset in the range. */
90
+ end: bigint;
91
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ // The shapes the checker reads a restoration into.
2
+ export {};
package/dist/yaml.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Problem } from "./problems.ts";
2
+ import type { Meta, Yaml } from "./types.ts";
3
+ /** Reads one scalar or flow list. */
4
+ export declare function parseScalar(raw: string): Yaml;
5
+ /** Reads front matter (or a manifest) into a map, reporting each line it cannot read against file. */
6
+ export declare function parseYaml(text: string, file: string, problem: Problem): Meta;
package/dist/yaml.js ADDED
@@ -0,0 +1,173 @@
1
+ // The front matter reader. It understands the subset of YAML the standard's front matter uses:
2
+ // scalars, flow lists, and block lists of flat maps.
3
+ /** Reads one scalar or flow list. */
4
+ export function parseScalar(raw) {
5
+ let v = raw.trim();
6
+ if (v === "")
7
+ return "";
8
+ if (v.startsWith('"')) {
9
+ const end = v.indexOf('"', 1);
10
+ return v.slice(1, end);
11
+ }
12
+ if (v.startsWith("'")) {
13
+ const end = v.indexOf("'", 1);
14
+ return v.slice(1, end);
15
+ }
16
+ const hash = v.search(/\s#/);
17
+ if (hash >= 0)
18
+ v = v.slice(0, hash).trim();
19
+ if (v.startsWith("[")) {
20
+ const inner = v.slice(1, v.lastIndexOf("]")).trim();
21
+ if (inner === "")
22
+ return [];
23
+ return splitFlow(inner).map((x) => parseScalar(x));
24
+ }
25
+ if (v === "null" || v === "~")
26
+ return null;
27
+ if (v === "true")
28
+ return true;
29
+ if (v === "false")
30
+ return false;
31
+ if (/^-?\d+$/.test(v))
32
+ return Number(v);
33
+ if (/^-?\d+\.\d+$/.test(v))
34
+ return Number(v);
35
+ return v;
36
+ }
37
+ function splitFlow(s) {
38
+ const out = [];
39
+ let cur = "";
40
+ let quote = null;
41
+ for (const ch of s) {
42
+ if (quote) {
43
+ cur += ch;
44
+ if (ch === quote)
45
+ quote = null;
46
+ }
47
+ else if (ch === '"' || ch === "'") {
48
+ quote = ch;
49
+ cur += ch;
50
+ }
51
+ else if (ch === ",") {
52
+ out.push(cur.trim());
53
+ cur = "";
54
+ }
55
+ else
56
+ cur += ch;
57
+ }
58
+ if (cur.trim() !== "")
59
+ out.push(cur.trim());
60
+ return out;
61
+ }
62
+ function stripComment(line) {
63
+ let quote = null;
64
+ for (let i = 0; i < line.length; i++) {
65
+ const ch = line[i];
66
+ if (quote) {
67
+ if (ch === quote)
68
+ quote = null;
69
+ }
70
+ else if (ch === '"' || ch === "'")
71
+ quote = ch;
72
+ else if (ch === "#" && (i === 0 || /\s/.test(line[i - 1])))
73
+ return line.slice(0, i).replace(/\s+$/, "");
74
+ }
75
+ return line.replace(/\s+$/, "");
76
+ }
77
+ /** Reads front matter (or a manifest) into a map, reporting each line it cannot read against file. */
78
+ export function parseYaml(text, file, problem) {
79
+ const lines = text.split(/\r?\n/).map(stripComment);
80
+ const obj = {};
81
+ let i = 0;
82
+ while (i < lines.length) {
83
+ const line = lines[i];
84
+ if (line.trim() === "") {
85
+ i++;
86
+ continue;
87
+ }
88
+ const m = /^([A-Za-z_][A-Za-z0-9_]*):(.*)$/.exec(line);
89
+ if (!m) {
90
+ problem(file, `unreadable front matter line: ${line}`);
91
+ i++;
92
+ continue;
93
+ }
94
+ const key = m[1];
95
+ const rest = m[2];
96
+ if (key in obj)
97
+ problem(file, `front matter repeats the field ${key}`);
98
+ if (rest.trim() !== "") {
99
+ obj[key] = parseScalar(rest);
100
+ i++;
101
+ continue;
102
+ }
103
+ // block list
104
+ const items = [];
105
+ i++;
106
+ let current = null;
107
+ let itemIndent = null;
108
+ while (i < lines.length && (lines[i].trim() === "" || /^\s/.test(lines[i]))) {
109
+ const l = lines[i];
110
+ if (l.trim() === "") {
111
+ i++;
112
+ continue;
113
+ }
114
+ const dash = /^(\s*)- (.*)$/.exec(l);
115
+ if (dash && (itemIndent === null || dash[1].length === itemIndent)) {
116
+ itemIndent = dash[1].length;
117
+ const body = dash[2];
118
+ const kv = /^([A-Za-z_][A-Za-z0-9_]*):(.*)$/.exec(body);
119
+ if (kv) {
120
+ current = {};
121
+ items.push(current);
122
+ if (kv[2].trim() !== "")
123
+ current[kv[1]] = parseScalar(kv[2]);
124
+ else
125
+ current[kv[1]] = parseNested();
126
+ }
127
+ else {
128
+ current = null;
129
+ items.push(parseScalar(body));
130
+ }
131
+ i++;
132
+ continue;
133
+ }
134
+ const kv = /^\s+([A-Za-z_][A-Za-z0-9_]*):(.*)$/.exec(l);
135
+ if (kv && current) {
136
+ if (kv[2].trim() !== "")
137
+ current[kv[1]] = parseScalar(kv[2]);
138
+ else {
139
+ i++;
140
+ current[kv[1]] = parseNestedFrom();
141
+ continue;
142
+ }
143
+ i++;
144
+ continue;
145
+ }
146
+ problem(file, `unreadable front matter line: ${l}`);
147
+ i++;
148
+ }
149
+ obj[key] = items;
150
+ }
151
+ return obj;
152
+ // A nested map under a list item (such as `unpacked:` of a packed file).
153
+ function parseNestedFrom() {
154
+ const nested = {};
155
+ const indent = /^(\s*)/.exec(lines[i])[1].length;
156
+ while (i < lines.length &&
157
+ lines[i].trim() !== "" &&
158
+ /^(\s*)/.exec(lines[i])[1].length >= indent &&
159
+ !/^\s*- /.test(lines[i])) {
160
+ const kv = /^\s+([A-Za-z_][A-Za-z0-9_]*):(.*)$/.exec(lines[i]);
161
+ if (kv)
162
+ nested[kv[1]] = parseScalar(kv[2]);
163
+ i++;
164
+ }
165
+ return nested;
166
+ }
167
+ function parseNested() {
168
+ i++;
169
+ const r = parseNestedFrom();
170
+ i--;
171
+ return r;
172
+ }
173
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scientific-method/standard-checker",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",