dotmd-cli 0.40.1 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/lifecycle.mjs +39 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotmd-cli",
3
- "version": "0.40.1",
3
+ "version": "0.40.2",
4
4
  "description": "CLI for managing markdown documents with YAML frontmatter — index, query, validate, graph, export, Notion sync, AI summaries.",
5
5
  "type": "module",
6
6
  "license": "MIT",
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, replaceFrontmatter } from './frontmatter.mjs';
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
- let raw = readFileSync(docFile, 'utf8');
833
- const { frontmatter: fm } = extractFrontmatter(raw);
834
- if (!fm || !fm.includes(basename)) continue;
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
- if (newFm !== fm) {
854
- raw = replaceFrontmatter(raw, newFm);
855
- writeFileSync(docFile, raw, 'utf8');
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
- const rebuilt = replaceFrontmatter(raw, newFm);
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
- const { frontmatter: fm } = extractFrontmatter(raw);
917
- if (!fm || !fm.includes(basename)) continue;
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
- if (fm.includes(oldRelPath) || fm.includes('./' + oldRelPath)) {
922
- count++;
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;