@plebeiantech/lat.md 0.12.3-fork.11 → 0.12.3-fork.13
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.
|
@@ -95,12 +95,40 @@ function inlineTextOf(node) {
|
|
|
95
95
|
})
|
|
96
96
|
.join('');
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* A GFM table that the parser handed back as a paragraph.
|
|
100
|
+
*
|
|
101
|
+
* `src/parser.ts` runs remark without `remark-gfm`, so a pipe table is never a
|
|
102
|
+
* `table` node — its rows arrive as one paragraph and every reference document
|
|
103
|
+
* containing a table failed with "this is a second paragraph", while the error
|
|
104
|
+
* text told the author to use a table. Adding the plugin would mean editing an
|
|
105
|
+
* upstream file and changing what `remarkStringify` emits for the whole tool,
|
|
106
|
+
* which `tests/roundtrip` pins; recognising the shape here costs nothing
|
|
107
|
+
* outside this check.
|
|
108
|
+
*
|
|
109
|
+
* The signature is a delimiter row — `| --- | :--: |` — under a line that opens
|
|
110
|
+
* a row. Both are required, because a delimiter row is what separates a table
|
|
111
|
+
* from prose that merely contains pipes.
|
|
112
|
+
*/
|
|
113
|
+
function isPipeTable(node, lines) {
|
|
114
|
+
const start = node.position?.start.line;
|
|
115
|
+
const end = node.position?.end.line;
|
|
116
|
+
if (!start || !end || end - start < 1)
|
|
117
|
+
return false;
|
|
118
|
+
const rows = lines.slice(start - 1, end);
|
|
119
|
+
return rows.some((row, i) => i > 0 &&
|
|
120
|
+
/^\s*\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?\s*$/.test(row) &&
|
|
121
|
+
rows[i - 1].trimStart().startsWith('|'));
|
|
122
|
+
}
|
|
98
123
|
/**
|
|
99
124
|
* Extra top-level paragraphs found under headings, beyond the one
|
|
100
125
|
* `checkSections` already permits as the leading summary. A paragraph
|
|
101
126
|
* before the first heading is the document lead, not narrative prose.
|
|
127
|
+
*
|
|
128
|
+
* A pipe table is not prose and never counts, in either position: it is
|
|
129
|
+
* neither the leading summary a reference is allowed nor an offender.
|
|
102
130
|
*/
|
|
103
|
-
function extraParagraphs(tree) {
|
|
131
|
+
function extraParagraphs(tree, lines) {
|
|
104
132
|
const offenders = [];
|
|
105
133
|
let currentHeading = null;
|
|
106
134
|
let seenParagraphUnderHeading = false;
|
|
@@ -111,6 +139,8 @@ function extraParagraphs(tree) {
|
|
|
111
139
|
continue;
|
|
112
140
|
}
|
|
113
141
|
if (node.type === 'paragraph') {
|
|
142
|
+
if (isPipeTable(node, lines))
|
|
143
|
+
continue;
|
|
114
144
|
if (currentHeading === null) {
|
|
115
145
|
// Document lead paragraph before any heading — allowed.
|
|
116
146
|
continue;
|
|
@@ -329,7 +359,7 @@ export async function checkMode(latticeDir, projectRoot = dirname(latticeDir), o
|
|
|
329
359
|
continue;
|
|
330
360
|
}
|
|
331
361
|
if (mode === 'reference') {
|
|
332
|
-
for (const offender of extraParagraphs(tree)) {
|
|
362
|
+
for (const offender of extraParagraphs(tree, content.split('\n'))) {
|
|
333
363
|
errors.push({
|
|
334
364
|
file: relPath,
|
|
335
365
|
line: offender.line,
|
package/package.json
CHANGED
|
@@ -17,15 +17,41 @@ enforces one shape rule for each:
|
|
|
17
17
|
| `lat.md/reference/` | lookup facts: fields, states, limits, names | no narrative prose — no second paragraph under a heading |
|
|
18
18
|
| `lat.md/explanation/` | why the design is what it is | no imperative commands |
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
A document is classified either by the directory it sits in, or by declaring
|
|
21
|
+
the mode in its own frontmatter:
|
|
22
|
+
|
|
23
|
+
```markdown
|
|
24
|
+
---
|
|
25
|
+
lat:
|
|
26
|
+
mode: explanation
|
|
27
|
+
---
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Both are enforced identically — `lat check mode` applies the shape rule to any
|
|
31
|
+
document that declares a mode, whether or not the root index carries
|
|
32
|
+
`require-mode: true`. That flag adds a separate rule: a document declaring *no*
|
|
33
|
+
mode, in no mode directory, becomes an error too.
|
|
34
|
+
|
|
35
|
+
Prefer the directory in a tree you own outright. Prefer the declaration when
|
|
36
|
+
the file cannot move — a document another project maintains, or a tree other
|
|
37
|
+
agents are writing to while you work. Moving costs every reference to the file;
|
|
38
|
+
declaring costs nothing at read time, because frontmatter sits above the first
|
|
39
|
+
heading and outside every section.
|
|
23
40
|
|
|
24
41
|
**A document that fails its mode is usually two documents.** A file that lists
|
|
25
42
|
the states *and* argues for the design is mixed: the lookup half goes in
|
|
26
43
|
`reference/`, the reasoning half in `explanation/`, and each links the other.
|
|
27
44
|
Filing it under one mode and softening the prose until the check passes is the
|
|
28
|
-
wrong repair.
|
|
45
|
+
wrong repair.
|
|
46
|
+
|
|
47
|
+
Classifying a mixed document does not make it pass. Narrative prose put under
|
|
48
|
+
`reference/` fails on contact, because a reference may not carry a second
|
|
49
|
+
paragraph under a heading — so a move is not a way to adopt the rule cheaply.
|
|
50
|
+
Split first, then place. And `explanation` is not the safe default: it bans
|
|
51
|
+
only imperative commands, so nearly any prose passes it. Declaring it is a
|
|
52
|
+
claim about the document, not somewhere to put whatever is left over. A
|
|
53
|
+
document you cannot honestly classify is better left undeclared than
|
|
54
|
+
mislabelled.
|
|
29
55
|
|
|
30
56
|
Classify before writing, not after. If a `diataxis` skill is available, use it.
|
|
31
57
|
|