dotmd-cli 0.40.0 → 0.40.2
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/bin/dotmd.mjs +14 -9
- package/package.json +1 -1
- package/src/lifecycle.mjs +39 -19
- package/src/render.mjs +23 -13
package/bin/dotmd.mjs
CHANGED
|
@@ -335,15 +335,19 @@ Use --dry-run (-n) to preview changes without writing anything.`,
|
|
|
335
335
|
|
|
336
336
|
check: `dotmd check — validate frontmatter and references
|
|
337
337
|
|
|
338
|
+
By default the warning list is suppressed: you see counts plus a one-line
|
|
339
|
+
pointer to \`dotmd doctor\` (auto-fix) or \`dotmd check --verbose\`
|
|
340
|
+
(per-doc detail). Errors are always shown in full.
|
|
341
|
+
|
|
338
342
|
Options:
|
|
339
|
-
--
|
|
343
|
+
--verbose Show every warning per-doc (with category collapse
|
|
344
|
+
applied — high-frequency auto-fixable categories
|
|
345
|
+
summarize to a one-line bulk-fix hint).
|
|
346
|
+
--no-collapse Like --verbose but disables category collapse too —
|
|
347
|
+
every warning prints raw.
|
|
348
|
+
--errors-only Show only errors, suppress warnings entirely
|
|
340
349
|
--fix Auto-fix broken refs, lint issues, and regenerate index
|
|
341
|
-
--json Output errors and warnings as JSON
|
|
342
|
-
--no-collapse Show every warning per-doc (since 0.37.0, high-frequency
|
|
343
|
-
auto-fixable warning categories — singular module/surface
|
|
344
|
-
deprecations, updated-behind-git — are collapsed into a
|
|
345
|
-
one-line summary with the bulk-fix command). --json output
|
|
346
|
-
is unchanged regardless.
|
|
350
|
+
--json Output errors and warnings as JSON (always full detail)
|
|
347
351
|
--dry-run, -n Preview fixes without writing (with --fix)`,
|
|
348
352
|
|
|
349
353
|
archive: `dotmd archive <file> — archive a document
|
|
@@ -1219,6 +1223,7 @@ async function main() {
|
|
|
1219
1223
|
const fix = args.includes('--fix');
|
|
1220
1224
|
const errorsOnly = args.includes('--errors-only');
|
|
1221
1225
|
const noCollapse = args.includes('--no-collapse');
|
|
1226
|
+
const verbose = args.includes('--verbose');
|
|
1222
1227
|
|
|
1223
1228
|
if (fix) {
|
|
1224
1229
|
// Auto-fix: broken refs, then lint, then rebuild index
|
|
@@ -1249,7 +1254,7 @@ async function main() {
|
|
|
1249
1254
|
passed: freshIndex.errors.length === 0,
|
|
1250
1255
|
}, null, 2) + '\n');
|
|
1251
1256
|
} else {
|
|
1252
|
-
process.stdout.write('\n' + renderCheck(freshIndex, config, { errorsOnly, noCollapse }));
|
|
1257
|
+
process.stdout.write('\n' + renderCheck(freshIndex, config, { errorsOnly, noCollapse, verbose }));
|
|
1253
1258
|
}
|
|
1254
1259
|
if (freshIndex.errors.length > 0) process.exitCode = 1;
|
|
1255
1260
|
return;
|
|
@@ -1268,7 +1273,7 @@ async function main() {
|
|
|
1268
1273
|
return;
|
|
1269
1274
|
}
|
|
1270
1275
|
|
|
1271
|
-
process.stdout.write(renderCheck(index, config, { errorsOnly, noCollapse }));
|
|
1276
|
+
process.stdout.write(renderCheck(index, config, { errorsOnly, noCollapse, verbose }));
|
|
1272
1277
|
if (index.errors.length > 0) process.exitCode = 1;
|
|
1273
1278
|
return;
|
|
1274
1279
|
}
|
package/package.json
CHANGED
package/src/lifecycle.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { extractFrontmatter, parseSimpleFrontmatter
|
|
3
|
+
import { extractFrontmatter, parseSimpleFrontmatter } from './frontmatter.mjs';
|
|
4
4
|
import { asString, toRepoPath, die, warn, resolveDocPath, resolveRefPath, escapeRegex, nowIso, suggestCandidates, emitFilesFooter } from './util.mjs';
|
|
5
5
|
import { gitMv, getGitLastModified, getGitLastModifiedBatch } from './git.mjs';
|
|
6
6
|
import { buildIndex, collectDocFiles } from './index.mjs';
|
|
@@ -829,30 +829,40 @@ function updateRefsAfterMove(oldPath, newPath, config) {
|
|
|
829
829
|
|
|
830
830
|
for (const docFile of allFiles) {
|
|
831
831
|
if (docFile === newPath) continue;
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
832
|
+
const raw = readFileSync(docFile, 'utf8');
|
|
833
|
+
if (!raw.includes(basename)) continue;
|
|
834
|
+
const { frontmatter: fm, body } = extractFrontmatter(raw);
|
|
835
|
+
if (!fm) continue;
|
|
835
836
|
|
|
836
837
|
const docDir = path.dirname(docFile);
|
|
837
838
|
const oldRelPath = path.relative(docDir, oldPath).split(path.sep).join('/');
|
|
838
839
|
const newRelPath = path.relative(docDir, newPath).split(path.sep).join('/');
|
|
839
840
|
|
|
840
841
|
let newFm = fm;
|
|
841
|
-
|
|
842
|
-
// Replace exact relative path
|
|
843
842
|
if (newFm.includes(oldRelPath)) {
|
|
844
843
|
newFm = newFm.split(oldRelPath).join(newRelPath);
|
|
845
844
|
}
|
|
846
|
-
|
|
847
|
-
// Also handle ./ prefix variant
|
|
848
845
|
const dotSlashOld = './' + oldRelPath;
|
|
849
846
|
if (newFm.includes(dotSlashOld)) {
|
|
850
847
|
newFm = newFm.split(dotSlashOld).join(newRelPath);
|
|
851
848
|
}
|
|
852
849
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
850
|
+
// Body markdown links [text](path.md) or [text](path.md#anchor) pointing
|
|
851
|
+
// at oldPath. resolveRefPath can't be used here: oldPath no longer exists
|
|
852
|
+
// on disk (git mv already ran), so its existsSync probe would fail. Match
|
|
853
|
+
// by resolving the href manually and comparing absolute paths instead.
|
|
854
|
+
const linkRegex = /(\[[^\]]*\]\()([^)#]+\.md)(#[^)]*)?(\))/g;
|
|
855
|
+
const newBody = body.replace(linkRegex, (match, pre, href, frag, post) => {
|
|
856
|
+
if (/^https?:/i.test(href)) return match;
|
|
857
|
+
const docRelAbs = path.resolve(docDir, href);
|
|
858
|
+
const repoRelAbs = path.resolve(config.repoRoot, href);
|
|
859
|
+
if (docRelAbs !== oldPath && repoRelAbs !== oldPath) return match;
|
|
860
|
+
const newHref = path.relative(docDir, newPath).split(path.sep).join('/');
|
|
861
|
+
return `${pre}${newHref}${frag ?? ''}${post}`;
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
if (newFm !== fm || newBody !== body) {
|
|
865
|
+
writeFileSync(docFile, `---\n${newFm}\n---\n${newBody}`, 'utf8');
|
|
856
866
|
touched.push(docFile);
|
|
857
867
|
}
|
|
858
868
|
}
|
|
@@ -895,10 +905,7 @@ function updateRefsFromMovedFile(oldPath, newPath, config) {
|
|
|
895
905
|
});
|
|
896
906
|
|
|
897
907
|
if (newFm !== frontmatter || newBody !== body) {
|
|
898
|
-
|
|
899
|
-
// Replace body: rebuilt has updated frontmatter but old body
|
|
900
|
-
const { frontmatter: updatedFm } = extractFrontmatter(rebuilt);
|
|
901
|
-
writeFileSync(newPath, `---\n${updatedFm}\n---${newBody}`, 'utf8');
|
|
908
|
+
writeFileSync(newPath, `---\n${newFm}\n---\n${newBody}`, 'utf8');
|
|
902
909
|
return 1;
|
|
903
910
|
}
|
|
904
911
|
|
|
@@ -913,14 +920,27 @@ function countRefsToUpdate(oldPath, newPath, config) {
|
|
|
913
920
|
for (const docFile of allFiles) {
|
|
914
921
|
if (docFile === newPath) continue;
|
|
915
922
|
const raw = readFileSync(docFile, 'utf8');
|
|
916
|
-
|
|
917
|
-
|
|
923
|
+
if (!raw.includes(basename)) continue;
|
|
924
|
+
const { frontmatter: fm, body } = extractFrontmatter(raw);
|
|
925
|
+
if (!fm) continue;
|
|
918
926
|
|
|
919
927
|
const docDir = path.dirname(docFile);
|
|
920
928
|
const oldRelPath = path.relative(docDir, oldPath).split(path.sep).join('/');
|
|
921
|
-
|
|
922
|
-
|
|
929
|
+
const fmHit = fm.includes(oldRelPath) || fm.includes('./' + oldRelPath);
|
|
930
|
+
|
|
931
|
+
let bodyHit = false;
|
|
932
|
+
if (!fmHit) {
|
|
933
|
+
const linkRegex = /\[[^\]]*\]\(([^)#]+\.md)(?:#[^)]*)?\)/g;
|
|
934
|
+
for (const match of body.matchAll(linkRegex)) {
|
|
935
|
+
const href = match[1];
|
|
936
|
+
if (/^https?:/i.test(href)) continue;
|
|
937
|
+
const docRelAbs = path.resolve(docDir, href);
|
|
938
|
+
const repoRelAbs = path.resolve(config.repoRoot, href);
|
|
939
|
+
if (docRelAbs === oldPath || repoRelAbs === oldPath) { bodyHit = true; break; }
|
|
940
|
+
}
|
|
923
941
|
}
|
|
942
|
+
|
|
943
|
+
if (fmHit || bodyHit) count++;
|
|
924
944
|
}
|
|
925
945
|
|
|
926
946
|
return count;
|
package/src/render.mjs
CHANGED
|
@@ -377,7 +377,7 @@ export function renderCheck(index, config, opts = {}) {
|
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
function _renderCheck(index, opts = {}) {
|
|
380
|
-
const { errorsOnly, noCollapse } = opts;
|
|
380
|
+
const { errorsOnly, noCollapse, verbose } = opts;
|
|
381
381
|
const lines = ['Check', ''];
|
|
382
382
|
lines.push(`- docs scanned: ${index.docs.length}`);
|
|
383
383
|
lines.push(`- errors: ${index.errors.length}`);
|
|
@@ -392,22 +392,32 @@ function _renderCheck(index, opts = {}) {
|
|
|
392
392
|
lines.push('');
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
// Warnings: terse by default — print count + pointer. The full per-doc list
|
|
396
|
+
// grew long enough on real projects that it drowned the summary; agents now
|
|
397
|
+
// see warnings as a single line and opt in to detail when they need it.
|
|
398
|
+
// --verbose / --no-collapse expand to the full list (with collapse-by-category
|
|
399
|
+
// still applied unless --no-collapse is set).
|
|
395
400
|
if (!errorsOnly && index.warnings.length > 0) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
401
|
+
if (verbose || noCollapse) {
|
|
402
|
+
lines.push(yellow('Warnings'));
|
|
403
|
+
if (noCollapse) {
|
|
404
|
+
for (const issue of index.warnings) {
|
|
405
|
+
lines.push(`- ${issue.path}: ${issue.message}`);
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
const { passthrough, collapsed } = categorizeWarnings(index.warnings);
|
|
409
|
+
for (const issue of passthrough) {
|
|
410
|
+
lines.push(`- ${issue.path}: ${issue.message}`);
|
|
411
|
+
}
|
|
412
|
+
for (const sum of collapsed) {
|
|
413
|
+
lines.push(`- ${sum.count} ${sum.label} — run \`${sum.fix}\` to bulk-fix`);
|
|
414
|
+
}
|
|
400
415
|
}
|
|
416
|
+
lines.push('');
|
|
401
417
|
} else {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
lines.push(`- ${issue.path}: ${issue.message}`);
|
|
405
|
-
}
|
|
406
|
-
for (const sum of collapsed) {
|
|
407
|
-
lines.push(`- ${sum.count} ${sum.label} — run \`${sum.fix}\` to bulk-fix`);
|
|
408
|
-
}
|
|
418
|
+
lines.push(dim(`Run \`dotmd check --verbose\` for per-doc detail, or \`dotmd doctor\` to auto-fix where possible.`));
|
|
419
|
+
lines.push('');
|
|
409
420
|
}
|
|
410
|
-
lines.push('');
|
|
411
421
|
}
|
|
412
422
|
|
|
413
423
|
if (index.errors.length === 0 && index.warnings.length === 0) {
|