dotmd-cli 0.8.0 → 0.8.1
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/package.json +1 -1
- package/src/extractors.mjs +2 -2
- package/src/lifecycle.mjs +2 -4
- package/src/lint.mjs +36 -0
- package/src/validate.mjs +3 -3
package/package.json
CHANGED
package/src/extractors.mjs
CHANGED
|
@@ -41,8 +41,8 @@ export function extractNextStep(body) {
|
|
|
41
41
|
|
|
42
42
|
export function extractBodyLinks(body) {
|
|
43
43
|
if (!body) return [];
|
|
44
|
-
// Strip fenced code blocks to avoid false positives
|
|
45
|
-
const stripped = body.replace(/^```[\s\S]*?^```/gm, '');
|
|
44
|
+
// Strip fenced code blocks and inline code to avoid false positives
|
|
45
|
+
const stripped = body.replace(/^```[\s\S]*?^```/gm, '').replace(/`[^`]+`/g, '');
|
|
46
46
|
const links = [];
|
|
47
47
|
// Match [text](path.md) or [text](path.md#anchor), skip images (preceded by !)
|
|
48
48
|
const regex = /(?<!!)\[([^\]]+)\]\(([^)]+\.md(?:#[^)]*)?)\)/g;
|
package/src/lifecycle.mjs
CHANGED
|
@@ -188,10 +188,8 @@ export function runTouch(argv, config, opts = {}) {
|
|
|
188
188
|
const gitDay = gitDate.slice(0, 10);
|
|
189
189
|
if (fmUpdated === gitDay) continue;
|
|
190
190
|
|
|
191
|
-
// Only sync if git is newer than frontmatter
|
|
192
|
-
|
|
193
|
-
const fmMs = fmUpdated ? new Date(fmUpdated).getTime() : 0;
|
|
194
|
-
if (fmMs >= gitMs) continue;
|
|
191
|
+
// Only sync if git is newer than frontmatter (compare date strings)
|
|
192
|
+
if (fmUpdated && fmUpdated >= gitDay) continue;
|
|
195
193
|
|
|
196
194
|
if (!dryRun) {
|
|
197
195
|
updateFrontmatter(filePath, { updated: gitDay });
|
package/src/lint.mjs
CHANGED
|
@@ -47,6 +47,13 @@ export function runLint(argv, config, opts = {}) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// Comma-separated surface → surfaces array
|
|
51
|
+
const surfaceVal = asString(parsed.surface);
|
|
52
|
+
if (surfaceVal && surfaceVal.includes(',')) {
|
|
53
|
+
const values = surfaceVal.split(',').map(s => s.trim()).filter(Boolean);
|
|
54
|
+
fixes.push({ field: 'surface', oldValue: surfaceVal, newValue: values, type: 'split-to-array' });
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
// Trailing whitespace in values
|
|
51
58
|
for (const line of frontmatter.split('\n')) {
|
|
52
59
|
const m = line.match(/^([A-Za-z0-9_-]+):(.+\S)\s+$/);
|
|
@@ -78,6 +85,8 @@ export function runLint(argv, config, opts = {}) {
|
|
|
78
85
|
for (const f of fixes) {
|
|
79
86
|
if (f.type === 'rename-key') {
|
|
80
87
|
process.stdout.write(dim(` ${f.oldValue} → ${f.newValue}\n`));
|
|
88
|
+
} else if (f.type === 'split-to-array') {
|
|
89
|
+
process.stdout.write(dim(` ${f.field}: "${f.oldValue}" → surfaces: [${f.newValue.join(', ')}]\n`));
|
|
81
90
|
} else if (f.type === 'eof') {
|
|
82
91
|
process.stdout.write(dim(` missing newline at end of file\n`));
|
|
83
92
|
} else if (f.type === 'add') {
|
|
@@ -112,6 +121,7 @@ export function runLint(argv, config, opts = {}) {
|
|
|
112
121
|
const keyRenames = [];
|
|
113
122
|
let needsEofFix = false;
|
|
114
123
|
const trimFixes = [];
|
|
124
|
+
const splitToArray = [];
|
|
115
125
|
|
|
116
126
|
for (const f of fixes) {
|
|
117
127
|
if (f.type === 'rename-key') {
|
|
@@ -120,12 +130,36 @@ export function runLint(argv, config, opts = {}) {
|
|
|
120
130
|
needsEofFix = true;
|
|
121
131
|
} else if (f.type === 'trim') {
|
|
122
132
|
trimFixes.push(f);
|
|
133
|
+
} else if (f.type === 'split-to-array') {
|
|
134
|
+
splitToArray.push(f);
|
|
123
135
|
} else {
|
|
124
136
|
updates[f.field] = f.newValue;
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
|
|
128
140
|
if (!dryRun) {
|
|
141
|
+
// Apply split-to-array fixes (surface: a, b → surfaces: array)
|
|
142
|
+
for (const sa of splitToArray) {
|
|
143
|
+
let raw = readFileSync(filePath, 'utf8');
|
|
144
|
+
const { frontmatter: fm } = extractFrontmatter(raw);
|
|
145
|
+
// Remove the scalar surface line
|
|
146
|
+
let newFm = fm.replace(new RegExp(`^${escapeRegex(sa.field)}:.*$`, 'm'), '').replace(/\n{2,}/g, '\n');
|
|
147
|
+
// Check if surfaces: array already exists
|
|
148
|
+
if (newFm.includes('surfaces:')) {
|
|
149
|
+
// Append new values to existing array
|
|
150
|
+
for (const val of sa.newValue) {
|
|
151
|
+
if (!newFm.includes(`- ${val}`)) {
|
|
152
|
+
newFm = newFm.replace(/^(surfaces:)$/m, `$1\n - ${val}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// Create new surfaces: array
|
|
157
|
+
newFm += `\nsurfaces:\n${sa.newValue.map(v => ` - ${v}`).join('\n')}`;
|
|
158
|
+
}
|
|
159
|
+
raw = replaceFrontmatter(raw, newFm.trim());
|
|
160
|
+
writeFileSync(filePath, raw, 'utf8');
|
|
161
|
+
}
|
|
162
|
+
|
|
129
163
|
// Apply key renames and trim fixes via raw string manipulation
|
|
130
164
|
if (keyRenames.length > 0 || trimFixes.length > 0) {
|
|
131
165
|
let raw = readFileSync(filePath, 'utf8');
|
|
@@ -165,6 +199,8 @@ export function runLint(argv, config, opts = {}) {
|
|
|
165
199
|
process.stdout.write(`${prefix} ${dim(`${f.oldValue} → ${f.newValue}`)}\n`);
|
|
166
200
|
} else if (f.type === 'eof') {
|
|
167
201
|
process.stdout.write(`${prefix} ${dim('added newline at EOF')}\n`);
|
|
202
|
+
} else if (f.type === 'split-to-array') {
|
|
203
|
+
process.stdout.write(`${prefix} ${dim(`${f.field}: "${f.oldValue}" → surfaces: [${f.newValue.join(', ')}]`)}\n`);
|
|
168
204
|
} else if (f.type === 'add') {
|
|
169
205
|
process.stdout.write(`${prefix} ${dim(`add ${f.field}: ${f.newValue}`)}\n`);
|
|
170
206
|
} else {
|
package/src/validate.mjs
CHANGED
|
@@ -10,7 +10,7 @@ export function validateDoc(doc, frontmatter, headingTitle, config) {
|
|
|
10
10
|
if (!doc.status) {
|
|
11
11
|
doc.errors.push({ path: doc.path, level: 'error', message: 'Missing frontmatter `status`.' });
|
|
12
12
|
} else if (!config.validStatuses.has(doc.status)) {
|
|
13
|
-
doc.
|
|
13
|
+
doc.warnings.push({ path: doc.path, level: 'warning', message: `Unknown status \`${doc.status}\`; not in statuses.order.` });
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
if (!config.lifecycle.skipWarningsFor.has(doc.status) && !doc.updated) {
|
|
@@ -126,8 +126,8 @@ export function checkGitStaleness(docs, config) {
|
|
|
126
126
|
const gitDate = getGitLastModified(doc.path, config.repoRoot);
|
|
127
127
|
if (!gitDate) continue;
|
|
128
128
|
|
|
129
|
-
const gitDay =
|
|
130
|
-
const fmDay =
|
|
129
|
+
const gitDay = gitDate.slice(0, 10);
|
|
130
|
+
const fmDay = doc.updated.slice(0, 10);
|
|
131
131
|
|
|
132
132
|
if (gitDay > fmDay) {
|
|
133
133
|
warnings.push({
|