( root: string, oldRel: string, targetFolder: NoteFolder, targetSubpath: string )
| 3654 | * collision), then re-reads meta for the new location. |
| 3655 | */ |
| 3656 | export async function moveNote( |
| 3657 | root: string, |
| 3658 | oldRel: string, |
| 3659 | targetFolder: NoteFolder, |
| 3660 | targetSubpath: string |
| 3661 | ): Promise<NoteMeta> { |
| 3662 | const oldAbs = resolveSafe(root, oldRel) |
| 3663 | const filename = path.basename(oldAbs) |
| 3664 | const cleanSub = targetSubpath.replace(/^\/+|\/+$/g, '') |
| 3665 | const targetRoot = await folderRoot(root, targetFolder) |
| 3666 | const destDir = cleanSub ? resolveSafe(targetRoot, cleanSub) : targetRoot |
| 3667 | |
| 3668 | // No-op if the source already lives at the destination. |
| 3669 | if (path.dirname(oldAbs) === destDir) { |
| 3670 | const folder = folderOf(root, oldAbs) |
| 3671 | if (!folder) throw new Error(`Note not in a known folder: ${oldRel}`) |
| 3672 | return await readMeta(root, oldAbs, folder) |
| 3673 | } |
| 3674 | |
| 3675 | await fs.mkdir(destDir, { recursive: true }) |
| 3676 | const ext = path.extname(filename) |
| 3677 | const baseTitle = path.basename(filename, ext) |
| 3678 | const finalTitle = await uniqueTitle(destDir, baseTitle) |
| 3679 | const destAbs = path.join(destDir, `${finalTitle}${ext}`) |
| 3680 | await fs.rename(oldAbs, destAbs) |
| 3681 | const meta = await readMeta(root, destAbs, targetFolder) |
| 3682 | await moveNoteComments(root, oldRel, meta.path) |
| 3683 | invalidateNoteMetaCache(root, oldRel) |
| 3684 | invalidateNoteMetaCache(root, meta.path) |
| 3685 | invalidateVaultTextSearchCache(root) |
| 3686 | return meta |
| 3687 | } |
| 3688 | |
| 3689 | export async function duplicateNote(root: string, rel: string): Promise<NoteMeta> { |
| 3690 | const abs = resolveSafe(root, rel) |
no test coverage detected