( root: string, abs: string, folder: NoteFolder, siblingOrder?: number, isSymlink?: boolean )
| 2359 | } |
| 2360 | |
| 2361 | async function readMeta( |
| 2362 | root: string, |
| 2363 | abs: string, |
| 2364 | folder: NoteFolder, |
| 2365 | siblingOrder?: number, |
| 2366 | isSymlink?: boolean |
| 2367 | ): Promise<NoteMeta> { |
| 2368 | const stat = await fs.stat(abs) |
| 2369 | const relPath = toPosix(path.relative(root, abs)) |
| 2370 | const cacheKey = noteMetaCacheKey(root, abs) |
| 2371 | const cached = noteMetaCache.get(cacheKey) |
| 2372 | const resolvedSiblingOrder = siblingOrder ?? (await readSiblingOrder(abs)) |
| 2373 | // stat() follows symlinks, so it can't tell us whether `abs` itself is a |
| 2374 | // link. The walk already knows from the readdir entry and passes it in; |
| 2375 | // fall back to lstat for callers that don't. Resolved fresh every call so |
| 2376 | // it can't go stale behind the body cache (like siblingOrder). |
| 2377 | let linked = isSymlink |
| 2378 | if (linked === undefined) { |
| 2379 | try { |
| 2380 | linked = (await fs.lstat(abs)).isSymbolicLink() |
| 2381 | } catch { |
| 2382 | linked = false |
| 2383 | } |
| 2384 | } |
| 2385 | if ( |
| 2386 | cached && |
| 2387 | sameMtimeMs(cached.mtimeMs, stat.mtimeMs) && |
| 2388 | cached.size === stat.size && |
| 2389 | cached.meta.path === relPath && |
| 2390 | cached.meta.folder === folder |
| 2391 | ) { |
| 2392 | return { ...cached.meta, siblingOrder: resolvedSiblingOrder, isSymlink: linked } |
| 2393 | } |
| 2394 | |
| 2395 | // Excalidraw drawings are JSON, not Markdown — don't parse their body for |
| 2396 | // tags/links/excerpt (that would be garbage) or even read it for meta. |
| 2397 | if (isExcalidrawPath(relPath)) { |
| 2398 | const meta: NoteMeta = { |
| 2399 | path: relPath, |
| 2400 | title: path.basename(abs, path.extname(abs)), |
| 2401 | folder, |
| 2402 | siblingOrder: resolvedSiblingOrder, |
| 2403 | createdAt: stat.birthtimeMs || stat.ctimeMs, |
| 2404 | updatedAt: stat.mtimeMs, |
| 2405 | size: stat.size, |
| 2406 | tags: [], |
| 2407 | wikilinks: [], |
| 2408 | assetEmbeds: [], |
| 2409 | hasAttachments: false, |
| 2410 | excerpt: '', |
| 2411 | isSymlink: linked |
| 2412 | } |
| 2413 | noteMetaCache.set(cacheKey, { mtimeMs: stat.mtimeMs, size: stat.size, meta }) |
| 2414 | return meta |
| 2415 | } |
| 2416 | |
| 2417 | let body = '' |
| 2418 | try { |
no test coverage detected