@nielspeter/eess-md 0.8.0 → 0.9.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 +3 -1
- package/dist/builders/declaration-findings.d.ts +37 -0
- package/dist/builders/declaration-findings.d.ts.map +1 -0
- package/dist/builders/declaration-findings.js +128 -0
- package/dist/builders/declaration-findings.js.map +1 -0
- package/dist/builders/links.d.ts +60 -1
- package/dist/builders/links.d.ts.map +1 -1
- package/dist/builders/links.js +92 -1
- package/dist/builders/links.js.map +1 -1
- package/dist/conditions/linked-back.d.ts +17 -0
- package/dist/conditions/linked-back.d.ts.map +1 -0
- package/dist/conditions/linked-back.js +122 -0
- package/dist/conditions/linked-back.js.map +1 -0
- package/dist/conditions/resolve.d.ts +8 -40
- package/dist/conditions/resolve.d.ts.map +1 -1
- package/dist/conditions/resolve.js +26 -138
- package/dist/conditions/resolve.js.map +1 -1
- package/dist/corpus.d.ts +3 -0
- package/dist/corpus.d.ts.map +1 -1
- package/dist/corpus.js +7 -10
- package/dist/corpus.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/model/document.d.ts +8 -1
- package/dist/model/document.d.ts.map +1 -1
- package/dist/model/document.js +14 -3
- package/dist/model/document.js.map +1 -1
- package/dist/model/label.d.ts +35 -0
- package/dist/model/label.d.ts.map +1 -0
- package/dist/model/label.js +68 -0
- package/dist/model/label.js.map +1 -0
- package/dist/model/links.d.ts +43 -0
- package/dist/model/links.d.ts.map +1 -1
- package/dist/model/links.js +112 -11
- package/dist/model/links.js.map +1 -1
- package/dist/model/prose.d.ts +11 -0
- package/dist/model/prose.d.ts.map +1 -1
- package/dist/model/prose.js +50 -5
- package/dist/model/prose.js.map +1 -1
- package/dist/model/query.d.ts +7 -0
- package/dist/model/query.d.ts.map +1 -1
- package/dist/model/query.js +10 -3
- package/dist/model/query.js.map +1 -1
- package/dist/model/resolve-link.d.ts +142 -0
- package/dist/model/resolve-link.d.ts.map +1 -0
- package/dist/model/resolve-link.js +194 -0
- package/dist/model/resolve-link.js.map +1 -0
- package/dist/predicates/declared-block.d.ts +29 -0
- package/dist/predicates/declared-block.d.ts.map +1 -0
- package/dist/predicates/declared-block.js +47 -0
- package/dist/predicates/declared-block.js.map +1 -0
- package/dist/predicates/live-target.d.ts +12 -0
- package/dist/predicates/live-target.d.ts.map +1 -0
- package/dist/predicates/live-target.js +17 -0
- package/dist/predicates/live-target.js.map +1 -0
- package/dist/rules/adr.d.ts +14 -5
- package/dist/rules/adr.d.ts.map +1 -1
- package/dist/rules/adr.js +5 -27
- package/dist/rules/adr.js.map +1 -1
- package/dist/rules/ledger.d.ts +10 -6
- package/dist/rules/ledger.d.ts.map +1 -1
- package/dist/rules/ledger.js +80 -24
- package/dist/rules/ledger.js.map +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
2
|
+
const matchersOf = new WeakMap();
|
|
3
|
+
/**
|
|
4
|
+
* Record how `corpus()` filtered its files. Called by `corpus()` only; a
|
|
5
|
+
* `Corpus` built any other way has no matchers, and an unloaded `.md` file in
|
|
6
|
+
* it is reported as `outside-roots`.
|
|
7
|
+
*/
|
|
8
|
+
export function registerCorpusMatchers(corpus, matchers) {
|
|
9
|
+
matchersOf.set(corpus, matchers);
|
|
10
|
+
}
|
|
11
|
+
const indexesOf = new WeakMap();
|
|
12
|
+
/**
|
|
13
|
+
* The two corpus-wide indexes resolution needs, built once per corpus. Before
|
|
14
|
+
* plan 0404 the directory index was rebuilt by every `resolve()` condition;
|
|
15
|
+
* called per link from a custom rule it would be rebuilt per link.
|
|
16
|
+
*
|
|
17
|
+
* The directory index is derived from `fileIndex` rather than new I/O: each
|
|
18
|
+
* indexed file's ancestors are known directories. `fileIndex` spans the whole
|
|
19
|
+
* physical repo walk from `cwd`, not just the configured `roots`, so a
|
|
20
|
+
* directory link can resolve against something outside any root. The repo
|
|
21
|
+
* root itself (`.`) is deliberately excluded — nobody links "the whole repo".
|
|
22
|
+
*/
|
|
23
|
+
function indexes(corpus) {
|
|
24
|
+
const cached = indexesOf.get(corpus);
|
|
25
|
+
if (cached !== undefined)
|
|
26
|
+
return cached;
|
|
27
|
+
const directories = new Set();
|
|
28
|
+
for (const rel of corpus.fileIndex) {
|
|
29
|
+
let dir = posix.dirname(rel);
|
|
30
|
+
while (dir !== '.' && dir !== '/' && !directories.has(dir)) {
|
|
31
|
+
directories.add(dir);
|
|
32
|
+
dir = posix.dirname(dir);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const documents = new Map();
|
|
36
|
+
for (const doc of corpus.documents())
|
|
37
|
+
documents.set(doc.relPath, doc);
|
|
38
|
+
const built = { directories, documents };
|
|
39
|
+
indexesOf.set(corpus, built);
|
|
40
|
+
return built;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A link URL's base repo-relative target(s), or `[]` for a pure fragment.
|
|
44
|
+
* Site-absolute links (leading `/`) yield the repo-root target, then the
|
|
45
|
+
* content-root target when `rootDir` is set. Throws `URIError` on malformed
|
|
46
|
+
* percent-encoding; `resolveLink` turns that into `missing`.
|
|
47
|
+
*
|
|
48
|
+
* Exported for `linkResolves`, which labels the two site-absolute candidates
|
|
49
|
+
* in its directory hint (bug 0137); not part of the package's public API.
|
|
50
|
+
*/
|
|
51
|
+
export function linkTargets(link, options) {
|
|
52
|
+
const withoutFragment = link.url.split('#')[0] ?? '';
|
|
53
|
+
if (withoutFragment === '')
|
|
54
|
+
return [];
|
|
55
|
+
const decoded = decodeURIComponent(withoutFragment);
|
|
56
|
+
if (decoded.startsWith('/')) {
|
|
57
|
+
const repoRooted = posix.normalize(decoded.replace(/^\/+/, ''));
|
|
58
|
+
return options.rootDir !== undefined
|
|
59
|
+
? [repoRooted, posix.normalize(posix.join(options.rootDir, repoRooted))]
|
|
60
|
+
: [repoRooted];
|
|
61
|
+
}
|
|
62
|
+
const base = posix.dirname(link.doc.relPath);
|
|
63
|
+
return [posix.normalize(posix.join(base, decoded))];
|
|
64
|
+
}
|
|
65
|
+
/** Every repo-relative file candidate for one target, in the order they are tried. */
|
|
66
|
+
function candidates(target, options) {
|
|
67
|
+
const out = [target];
|
|
68
|
+
const bare = target.replace(/\/+$/, '');
|
|
69
|
+
for (const ext of options.tryExtensions ?? []) {
|
|
70
|
+
out.push(bare + ext);
|
|
71
|
+
}
|
|
72
|
+
if (options.tryIndex !== undefined) {
|
|
73
|
+
out.push(posix.join(bare, options.tryIndex));
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function fileTarget(path, link, corpus) {
|
|
78
|
+
if (path === link.doc.relPath)
|
|
79
|
+
return { kind: 'self' };
|
|
80
|
+
const doc = indexes(corpus).documents.get(path);
|
|
81
|
+
if (doc !== undefined)
|
|
82
|
+
return { kind: 'document', path, doc };
|
|
83
|
+
const matchers = matchersOf.get(corpus);
|
|
84
|
+
const reasons = [];
|
|
85
|
+
if (!path.endsWith('.md'))
|
|
86
|
+
reasons.push('not-markdown');
|
|
87
|
+
if (matchers?.ignored(path) === true)
|
|
88
|
+
reasons.push('ignored');
|
|
89
|
+
if (matchers === undefined || !matchers.inRoots(path))
|
|
90
|
+
reasons.push('outside-roots');
|
|
91
|
+
// A loaded `corpus()` reads exactly the `.md` files in its roots that it does
|
|
92
|
+
// not ignore, so one of the three always applies. Should a hand-built
|
|
93
|
+
// `Corpus` disagree, `outside-roots` is the reason that names a remedy.
|
|
94
|
+
if (reasons.length === 0)
|
|
95
|
+
reasons.push('outside-roots');
|
|
96
|
+
return { kind: 'not-in-corpus', path, reasons };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve a link to the one case it is (plan 0404). Total: it never throws.
|
|
100
|
+
*
|
|
101
|
+
* Candidates are tried in a fixed order — each target (repo-root, then
|
|
102
|
+
* content-root when `rootDir` is set), and within it the URL as written, then
|
|
103
|
+
* `tryExtensions`, then `tryIndex`. The first existing **file** wins. Only when
|
|
104
|
+
* no candidate is a file is a directory reported, the first target's first.
|
|
105
|
+
* When nothing exists, `missing.tried` is every candidate tried, in order.
|
|
106
|
+
*
|
|
107
|
+
* Links into the built-in ignores (`node_modules`, `.git`, `dist`, …) are
|
|
108
|
+
* `missing`: `corpus()` never walks those folders, so their files are not known.
|
|
109
|
+
*
|
|
110
|
+
* The directory and document indexes are built on a corpus's first resolution
|
|
111
|
+
* and cached. A `corpus()` result never changes, so that is exact; a hand-built
|
|
112
|
+
* `Corpus` whose `documents()` returns something different later is not re-read.
|
|
113
|
+
*/
|
|
114
|
+
export function resolveLink(link, corpus, options = {}) {
|
|
115
|
+
if (link.external)
|
|
116
|
+
return { kind: 'external' };
|
|
117
|
+
let targets;
|
|
118
|
+
try {
|
|
119
|
+
targets = linkTargets(link, options);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
// Malformed percent-encoding (`%E0`): the URL names no file anyone can
|
|
123
|
+
// reach, which is exactly `missing`. Reported, never thrown, so one bad
|
|
124
|
+
// link cannot abort a whole run.
|
|
125
|
+
void error;
|
|
126
|
+
return { kind: 'missing', tried: [link.url] };
|
|
127
|
+
}
|
|
128
|
+
if (targets.length === 0)
|
|
129
|
+
return { kind: 'self' };
|
|
130
|
+
const tried = [];
|
|
131
|
+
for (const target of targets) {
|
|
132
|
+
for (const candidate of candidates(target, options)) {
|
|
133
|
+
tried.push(candidate);
|
|
134
|
+
if (corpus.fileIndex.has(candidate))
|
|
135
|
+
return fileTarget(candidate, link, corpus);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const { directories } = indexes(corpus);
|
|
139
|
+
for (const target of targets) {
|
|
140
|
+
const bare = target.replace(/\/+$/, '');
|
|
141
|
+
if (directories.has(bare))
|
|
142
|
+
return { kind: 'directory', path: bare };
|
|
143
|
+
}
|
|
144
|
+
return { kind: 'missing', tried };
|
|
145
|
+
}
|
|
146
|
+
/** Basename → every repo file with it, for {@link movedLinkFix}. Built once per caller. */
|
|
147
|
+
export function basenameIndex(corpus) {
|
|
148
|
+
const byBasename = new Map();
|
|
149
|
+
for (const rel of corpus.fileIndex) {
|
|
150
|
+
const base = rel.slice(rel.lastIndexOf('/') + 1);
|
|
151
|
+
const list = byBasename.get(base);
|
|
152
|
+
if (list)
|
|
153
|
+
list.push(rel);
|
|
154
|
+
else
|
|
155
|
+
byBasename.set(base, [rel]);
|
|
156
|
+
}
|
|
157
|
+
return byBasename;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* If a broken link's basename uniquely names one file in the repo (the target
|
|
161
|
+
* moved, not renamed), a deterministic autofix rewriting the URL to a path
|
|
162
|
+
* relative to the linking document (plan 0066). Ambiguous basename, or a URL
|
|
163
|
+
* whose exact span isn't known, → no fix.
|
|
164
|
+
*
|
|
165
|
+
* Lives beside `resolveLink` so that path arithmetic stays in this one module
|
|
166
|
+
* (plan 0404's structural rule keeps `node:path` out of the conditions).
|
|
167
|
+
*/
|
|
168
|
+
export function movedLinkFix(link, byBasename) {
|
|
169
|
+
if (link.urlStart === undefined || link.urlEnd === undefined)
|
|
170
|
+
return undefined;
|
|
171
|
+
const path = link.url.split('#')[0] ?? '';
|
|
172
|
+
const fragment = link.url.slice(path.length); // '' or '#anchor'
|
|
173
|
+
const base = path.slice(path.replace(/\/+$/, '').lastIndexOf('/') + 1);
|
|
174
|
+
const matches = byBasename.get(base) ?? [];
|
|
175
|
+
if (matches.length !== 1)
|
|
176
|
+
return undefined; // renamed (no match) or ambiguous → no fix
|
|
177
|
+
const target = matches[0];
|
|
178
|
+
if (target === undefined)
|
|
179
|
+
return undefined;
|
|
180
|
+
let rel = posix.relative(posix.dirname(link.doc.relPath), target);
|
|
181
|
+
if (!rel.startsWith('.'))
|
|
182
|
+
rel = './' + rel;
|
|
183
|
+
const replacement = rel + fragment;
|
|
184
|
+
if (replacement === link.url)
|
|
185
|
+
return undefined;
|
|
186
|
+
return {
|
|
187
|
+
file: link.doc.file,
|
|
188
|
+
start: link.urlStart,
|
|
189
|
+
end: link.urlEnd,
|
|
190
|
+
replacement,
|
|
191
|
+
describe: `rewrite link "${link.url}" → "${replacement}"`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=resolve-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-link.js","sourceRoot":"","sources":["../../src/model/resolve-link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Predicate } from '@nielspeter/eess';
|
|
2
|
+
import type { MdLink, MdLinkBlock } from '../model/links.js';
|
|
3
|
+
/**
|
|
4
|
+
* Plan 0405 — select the links a block declares.
|
|
5
|
+
*
|
|
6
|
+
* These factories are deliberately NOT exported from the package root, the same
|
|
7
|
+
* as `areInternal`/`areLive`: the declaration scan lives in `LinkRuleBuilder`,
|
|
8
|
+
* which records the declaration when `areLabelled()`/`areInSection()` is called.
|
|
9
|
+
* A raw predicate composed through `satisfy()`/`not()`/`or()` would select
|
|
10
|
+
* without recording anything, and so without the scan.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The block that decides whether a link with this `path` is declared under the
|
|
14
|
+
* label `declared` matches: walking from the innermost block outwards, the first
|
|
15
|
+
* that either matches it (any form, exact case) or opens with a wrapped label of
|
|
16
|
+
* its own. A plain line that is not the label — `bug 0402: [b](b.md)`,
|
|
17
|
+
* `Metadata:`, a URL — never decides, so it neither hides a declaration nor
|
|
18
|
+
* captures one.
|
|
19
|
+
*/
|
|
20
|
+
export declare function decidingBlock(path: readonly MdLinkBlock[], declared: RegExp): {
|
|
21
|
+
readonly block: MdLinkBlock;
|
|
22
|
+
readonly at: number;
|
|
23
|
+
readonly matches: boolean;
|
|
24
|
+
} | undefined;
|
|
25
|
+
/** Links declared under `label` (case-sensitive), in any label form, at any depth below it. */
|
|
26
|
+
export declare function areLabelledPredicate(label: string): Predicate<MdLink>;
|
|
27
|
+
/** Links under a heading matching `name` (a string exactly, or a `RegExp`), at any depth beneath it. */
|
|
28
|
+
export declare function areInSectionPredicate(name: string | RegExp): Predicate<MdLink>;
|
|
29
|
+
//# sourceMappingURL=declared-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declared-block.d.ts","sourceRoot":"","sources":["../../src/predicates/declared-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAI5D;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAQ7F;AAED,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAMrE;AAED,wGAAwG;AACxG,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAK9E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { labelPattern, wrappedLabelOf } from '../model/label.js';
|
|
2
|
+
import { inSection } from '../model/query.js';
|
|
3
|
+
/**
|
|
4
|
+
* Plan 0405 — select the links a block declares.
|
|
5
|
+
*
|
|
6
|
+
* These factories are deliberately NOT exported from the package root, the same
|
|
7
|
+
* as `areInternal`/`areLive`: the declaration scan lives in `LinkRuleBuilder`,
|
|
8
|
+
* which records the declaration when `areLabelled()`/`areInSection()` is called.
|
|
9
|
+
* A raw predicate composed through `satisfy()`/`not()`/`or()` would select
|
|
10
|
+
* without recording anything, and so without the scan.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The block that decides whether a link with this `path` is declared under the
|
|
14
|
+
* label `declared` matches: walking from the innermost block outwards, the first
|
|
15
|
+
* that either matches it (any form, exact case) or opens with a wrapped label of
|
|
16
|
+
* its own. A plain line that is not the label — `bug 0402: [b](b.md)`,
|
|
17
|
+
* `Metadata:`, a URL — never decides, so it neither hides a declaration nor
|
|
18
|
+
* captures one.
|
|
19
|
+
*/
|
|
20
|
+
export function decidingBlock(path, declared) {
|
|
21
|
+
for (let at = path.length - 1; at >= 0; at--) {
|
|
22
|
+
const block = path[at];
|
|
23
|
+
if (block === undefined)
|
|
24
|
+
continue;
|
|
25
|
+
if (declared.test(block.text))
|
|
26
|
+
return { block, at, matches: true };
|
|
27
|
+
if (wrappedLabelOf(block.text) !== undefined)
|
|
28
|
+
return { block, at, matches: false };
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
/** Links declared under `label` (case-sensitive), in any label form, at any depth below it. */
|
|
33
|
+
export function areLabelledPredicate(label) {
|
|
34
|
+
const re = new RegExp(labelPattern(label));
|
|
35
|
+
return {
|
|
36
|
+
description: `are declared under the label "${label}"`,
|
|
37
|
+
test: (l) => decidingBlock(l.blockPath ?? [], re)?.matches === true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Links under a heading matching `name` (a string exactly, or a `RegExp`), at any depth beneath it. */
|
|
41
|
+
export function areInSectionPredicate(name) {
|
|
42
|
+
return {
|
|
43
|
+
description: `are in section "${String(name)}"`,
|
|
44
|
+
test: (l) => inSection(l, name),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=declared-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declared-block.js","sourceRoot":"","sources":["../../src/predicates/declared-block.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,IAA4B,EAC5B,QAAgB;IAEhB,KAAK,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;QACtB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAClE,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IACpF,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;IAC1C,OAAO;QACL,WAAW,EAAE,iCAAiC,KAAK,GAAG;QACtD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,OAAO,KAAK,IAAI;KACpE,CAAA;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,qBAAqB,CAAC,IAAqB;IACzD,OAAO;QACL,WAAW,EAAE,mBAAmB,MAAM,CAAC,IAAI,CAAC,GAAG;QAC/C,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;KAChC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Predicate } from '@nielspeter/eess';
|
|
2
|
+
import type { Corpus } from '../corpus.js';
|
|
3
|
+
import type { MdLink } from '../model/links.js';
|
|
4
|
+
import { type LinkResolveOptions } from '../model/resolve-link.js';
|
|
5
|
+
/**
|
|
6
|
+
* Links whose target is not a frozen corpus document (plan 0406), resolved with
|
|
7
|
+
* `resolveLink` and `options` — pass `beLinkedBack()` the same options, so both
|
|
8
|
+
* resolve a link the same way. Every other case is kept: a missing or external
|
|
9
|
+
* target is still examined, and still reported.
|
|
10
|
+
*/
|
|
11
|
+
export declare function haveLiveTargetsPredicate(corpus: Corpus, options?: LinkResolveOptions): Predicate<MdLink>;
|
|
12
|
+
//# sourceMappingURL=live-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-target.d.ts","sourceRoot":"","sources":["../../src/predicates/live-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAe,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE/E;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,SAAS,CAAC,MAAM,CAAC,CAQnB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { resolveLink } from '../model/resolve-link.js';
|
|
2
|
+
/**
|
|
3
|
+
* Links whose target is not a frozen corpus document (plan 0406), resolved with
|
|
4
|
+
* `resolveLink` and `options` — pass `beLinkedBack()` the same options, so both
|
|
5
|
+
* resolve a link the same way. Every other case is kept: a missing or external
|
|
6
|
+
* target is still examined, and still reported.
|
|
7
|
+
*/
|
|
8
|
+
export function haveLiveTargetsPredicate(corpus, options = {}) {
|
|
9
|
+
return {
|
|
10
|
+
description: 'have live targets (not in a frozen folder)',
|
|
11
|
+
test: (link) => {
|
|
12
|
+
const target = resolveLink(link, corpus, options);
|
|
13
|
+
return !(target.kind === 'document' && target.doc.frozen);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=live-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-target.js","sourceRoot":"","sources":["../../src/predicates/live-target.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAA2B,MAAM,0BAA0B,CAAA;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAc,EACd,UAA8B,EAAE;IAEhC,OAAO;QACL,WAAW,EAAE,4CAA4C;QACzD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;YACjD,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC3D,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/rules/adr.d.ts
CHANGED
|
@@ -19,19 +19,28 @@ export interface AdrEnforcementOptions extends PresetBaseOptions {
|
|
|
19
19
|
};
|
|
20
20
|
/** Valid tier numbers. Default `[1,2,3,4,5]`. */
|
|
21
21
|
readonly tiers?: readonly number[];
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Verify that the file paths a Mechanism cell cites exist. Default `true`.
|
|
24
|
+
*
|
|
25
|
+
* A cited `it('…')` **title** is not resolved here. Whether it names a real test is
|
|
26
|
+
* `eess-crossvalidate`'s `adrCitationsResolve`, which reads the test AST: a markdown
|
|
27
|
+
* dialect reading test files as text matched `it('r')` against any test beginning with
|
|
28
|
+
* `r`, and could not tell a commented-out test from a live one (bug 0111). Run that
|
|
29
|
+
* check too if your ADRs cite test titles.
|
|
30
|
+
*/
|
|
23
31
|
readonly verifyCitations?: boolean;
|
|
24
32
|
}
|
|
25
33
|
/**
|
|
26
34
|
* OPINIONATED preset: gate a repo's ADRs on the EESS enforcement-tier model.
|
|
27
35
|
* Gates on *declaration* — an ADR fails for a missing `## Enforcement` table, a
|
|
28
|
-
* clause with no valid tier, or (when `verifyCitations`) a
|
|
29
|
-
*
|
|
36
|
+
* clause with no valid tier, or (when `verifyCitations`) a cited file path that
|
|
37
|
+
* doesn't exist; a soft-tier clause declared as such passes.
|
|
30
38
|
*
|
|
31
39
|
* Emits per-rule ids so `overrides` can downgrade/disable individual checks.
|
|
32
40
|
*
|
|
33
|
-
* The citation check resolves
|
|
34
|
-
*
|
|
41
|
+
* The citation check resolves one form, a backticked file path. A cited `it('…')`
|
|
42
|
+
* title is `eess-crossvalidate`'s `adrCitationsResolve` to resolve, against the
|
|
43
|
+
* test AST (bug 0111). A Mechanism cell that cites something else (a rule id in
|
|
35
44
|
* your own architecture tool, a CI job) is not resolved here, and this preset
|
|
36
45
|
* takes no plugin for it: what counts as a live id is your fact, not the
|
|
37
46
|
* corpus's. Compose that check from `rows()` + `correspondence()` against the
|
package/dist/rules/adr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adr.d.ts","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adr.d.ts","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAW1C;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,uDAAuD;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAClC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACtE,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CACnC;AA8BD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,aAAa,EAAE,CAgDjB"}
|
package/dist/rules/adr.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
1
|
import { dispatchRule, finishPreset, validateOverrides, not, mergeCollectResults, } from '@nielspeter/eess';
|
|
4
2
|
import { docs } from '../builders/docs.js';
|
|
5
3
|
import { haveNameMatching } from '../predicates/document.js';
|
|
@@ -11,15 +9,6 @@ import { haveTableRowsSatisfying } from '../conditions/table-rows.js';
|
|
|
11
9
|
const notIndexDoc = not(haveNameMatching(/^(README|index)\.md$/i));
|
|
12
10
|
const RULE_IDS = ['adr/enforcement-declared', 'adr/valid-tiers', 'adr/citations-resolve'];
|
|
13
11
|
const PATH_RE = /`([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)`/g;
|
|
14
|
-
const IT_CITE_RE = /it(?:\.\w+)?\(\s*['"]([^'"]+)['"]/g;
|
|
15
|
-
/** Does `testFile` define a test titled `title`? (Text-level; 0059 upgrades to AST.) */
|
|
16
|
-
function testDefinesIt(testAbs, title) {
|
|
17
|
-
if (!existsSync(testAbs))
|
|
18
|
-
return false;
|
|
19
|
-
const content = readFileSync(testAbs, 'utf8');
|
|
20
|
-
const esc = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
21
|
-
return new RegExp(`it(?:\\.\\w+)?\\(\\s*['"\`]${esc}`).test(content);
|
|
22
|
-
}
|
|
23
12
|
function validateTier(ctx, tiers) {
|
|
24
13
|
const clause = ctx.cells[0] ?? '';
|
|
25
14
|
const match = ctx.get('tier').match(/[1-5]/);
|
|
@@ -32,38 +21,27 @@ function validateCitations(ctx, corpus) {
|
|
|
32
21
|
const clause = ctx.cells[0] ?? '';
|
|
33
22
|
const mech = ctx.get('mechanism');
|
|
34
23
|
const problems = [];
|
|
35
|
-
const citedPaths = [];
|
|
36
24
|
for (const m of mech.matchAll(PATH_RE)) {
|
|
37
25
|
const p = m[1];
|
|
38
26
|
if (p === undefined)
|
|
39
27
|
continue;
|
|
40
|
-
citedPaths.push(p);
|
|
41
28
|
if (!corpus.fileIndex.has(p)) {
|
|
42
29
|
problems.push(`clause "${clause}" cites missing file \`${p}\``);
|
|
43
30
|
}
|
|
44
31
|
}
|
|
45
|
-
const testFiles = citedPaths.filter((p) => p.endsWith('.ts'));
|
|
46
|
-
for (const m of mech.matchAll(IT_CITE_RE)) {
|
|
47
|
-
const title = m[1];
|
|
48
|
-
if (title === undefined)
|
|
49
|
-
continue;
|
|
50
|
-
const found = testFiles.some((p) => testDefinesIt(join(corpus.root, p), title));
|
|
51
|
-
if (!found) {
|
|
52
|
-
problems.push(`clause "${clause}" cites it('${title}') not found in ${testFiles.length ? testFiles.join(', ') : 'any cited test file'}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
32
|
return problems;
|
|
56
33
|
}
|
|
57
34
|
/**
|
|
58
35
|
* OPINIONATED preset: gate a repo's ADRs on the EESS enforcement-tier model.
|
|
59
36
|
* Gates on *declaration* — an ADR fails for a missing `## Enforcement` table, a
|
|
60
|
-
* clause with no valid tier, or (when `verifyCitations`) a
|
|
61
|
-
*
|
|
37
|
+
* clause with no valid tier, or (when `verifyCitations`) a cited file path that
|
|
38
|
+
* doesn't exist; a soft-tier clause declared as such passes.
|
|
62
39
|
*
|
|
63
40
|
* Emits per-rule ids so `overrides` can downgrade/disable individual checks.
|
|
64
41
|
*
|
|
65
|
-
* The citation check resolves
|
|
66
|
-
*
|
|
42
|
+
* The citation check resolves one form, a backticked file path. A cited `it('…')`
|
|
43
|
+
* title is `eess-crossvalidate`'s `adrCitationsResolve` to resolve, against the
|
|
44
|
+
* test AST (bug 0111). A Mechanism cell that cites something else (a rule id in
|
|
67
45
|
* your own architecture tool, a CI job) is not resolved here, and this preset
|
|
68
46
|
* takes no plugin for it: what counts as a live id is your fact, not the
|
|
69
47
|
* corpus's. Compose that check from `rows()` + `correspondence()` against the
|
package/dist/rules/adr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adr.js","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"adr.js","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/ledger.d.ts
CHANGED
|
@@ -48,15 +48,19 @@ export interface HonestyAtCloseOptions extends PresetReportOptions {
|
|
|
48
48
|
readonly closeInPlace?: boolean;
|
|
49
49
|
/**
|
|
50
50
|
* The full `State:` vocabulary this corpus uses. A token outside it is
|
|
51
|
-
* **reported** (`ledger/unknown-state`), not ignored.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
* **reported** (`ledger/unknown-state`), not ignored. A bug-shaped lane passes its own,
|
|
52
|
+
* e.g. `['Draft', 'Ready', 'Fixed', 'Rejected', 'Parked']`.
|
|
53
|
+
*
|
|
54
|
+
* **Declare it together with {@link terminalStates}, or omit both** for the plan lane's
|
|
55
|
+
* defaults. One alone throws an `ArchConfigError` (bug 0284).
|
|
54
56
|
*/
|
|
55
57
|
readonly states?: readonly string[];
|
|
56
58
|
/**
|
|
57
|
-
* Which of {@link states} mean *closed
|
|
58
|
-
*
|
|
59
|
-
*
|
|
59
|
+
* Which of {@link states} mean *closed*; a bug lane passes `['Fixed', 'Rejected']`.
|
|
60
|
+
* Members are treated as known states whether or not they also appear in {@link states}.
|
|
61
|
+
*
|
|
62
|
+
* **Declare it together with {@link states}, or omit both** for the defaults
|
|
63
|
+
* (`['Done', "Won't-do"]`). One alone throws an `ArchConfigError` (bug 0284).
|
|
60
64
|
*/
|
|
61
65
|
readonly terminalStates?: readonly string[];
|
|
62
66
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/rules/ledger.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/rules/ledger.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAOjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CACtC;AAqHD;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,IAAI,CAAC,EAAE,IAAI,GACV;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAItD;AA2VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,aAAa,EAAE,CA4EjB;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,wEAAwE;IACxE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,qFAAqF;IACrF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,WAAW,CAoB5F"}
|