MCPcopy Create free account
hub / github.com/ZenNotes/zennotes / migrateLooseAssets

Function migrateLooseAssets

apps/desktop/src/main/vault.ts:1228–1304  ·  view source on GitHub ↗
(
  root: string
)

Source from the content-addressed store, hash-verified

1226 * open. Returns the moved (assets-relative) and skipped (source) paths.
1227 */
1228export async function migrateLooseAssets(
1229 root: string
1230): Promise<{ moved: string[]; skipped: string[] }> {
1231 const moved: string[] = []
1232 const skipped: string[] = []
1233 const assetsAbs = path.join(root, ASSETS_DIR)
1234
1235 const moveIntoAssets = async (srcRel: string): Promise<void> => {
1236 const destRel = `${ASSETS_DIR}/${path.basename(srcRel)}`
1237 if (toPosix(srcRel) === destRel) return
1238 const destAbs = resolveSafe(root, destRel)
1239 try {
1240 await fs.access(destAbs) // a same-named asset already exists — don't clobber/rename
1241 skipped.push(toPosix(srcRel))
1242 return
1243 } catch {
1244 /* destination free */
1245 }
1246 await fs.mkdir(assetsAbs, { recursive: true })
1247 await fs.rename(resolveSafe(root, srcRel), destAbs)
1248 moved.push(destRel)
1249 }
1250
1251 // 1. Loose asset files sitting directly at the vault root.
1252 //
1253 // Skip this for a vault managed by another app — an `.obsidian/` directory
1254 // marks an Obsidian vault. Those loose root files (`.canvas`, images, PDFs,
1255 // anything) are the user's own, not ZenNotes' legacy attachments, and
1256 // silently relocating them into `assets/` surprised users who pointed
1257 // ZenNotes at an existing Obsidian vault (#202). The legacy ZenNotes
1258 // attachment dirs below never exist in such a vault, so they stay safe.
1259 let isExternalVault = false
1260 try {
1261 await fs.access(path.join(root, '.obsidian'))
1262 isExternalVault = true
1263 } catch {
1264 /* not an Obsidian vault */
1265 }
1266 if (!isExternalVault) {
1267 let rootEntries: Dirent[] = []
1268 try {
1269 rootEntries = await fs.readdir(root, { withFileTypes: true })
1270 } catch {
1271 rootEntries = []
1272 }
1273 for (const entry of rootEntries) {
1274 if (entry.name.startsWith('.') || !entry.isFile()) continue
1275 if (entry.name.toLowerCase().endsWith('.md')) continue // a note
1276 if (databaseCsvPathFor(entry.name) || isDatabaseInternalPath(entry.name)) continue // a database
1277 await moveIntoAssets(entry.name)
1278 }
1279 }
1280
1281 // 2. Files in the legacy attachment dirs, then drop the dir if it empties.
1282 for (const dir of LEGACY_ATTACHMENTS_DIRS) {
1283 let entries: Dirent[]
1284 try {
1285 entries = await fs.readdir(path.join(root, dir), { withFileTypes: true })

Callers 3

ensureVaultLayoutFunction · 0.85

Calls 3

databaseCsvPathForFunction · 0.90
isDatabaseInternalPathFunction · 0.90
moveIntoAssetsFunction · 0.85

Tested by

no test coverage detected