Ilence Ye

同步 Obsidian 到 Astro

我是看了 这篇文章 后有所启发。它说的是作者自己实现了一个脚本,用来将 Obsidian vault 上某个目录的笔记自动拷贝到 Astro site 里的指定目录下,这样作者就不用手动拷贝,这大大简化了博客的发布流程。

我的 til 网站 和作者采用的架构是一样的,也是用 Obsidian 来写内容,用 Astro 来创建网站。之前每次写了新的 TIL 后,我都要手动将位于 Obsidian 上的笔记拷贝到 Astro 项目里对应的 src/content/tils 目录下,这点真的很烦,无形中让我越来越不喜欢去写 TIL,因为我很讨厌后面的这个手动拷贝的过程。

所以这次看到 Rach Smith 的这篇文章,我抽空就试了试。然后我发现,她这个方法需要你先运行起脚本,然后这脚本开始监听指定目录下文件的变化,如果你在 Obsidian vault 的指定目录下新增了文件或改动了文件,就会在 Astro site 上自动同步。也就是说,这需要你先运行脚本,然后在开始在 Obsidian 里写内容。这个方式跟我预期的不一样,我预期的是先在 Obsidian 里写内容,写完之后再运行脚本,然后脚本会识别到新增的文件或改动的文件,并将它们同步到 Astro site 里。

于是我按我的思路自己实现了一下。最后的代码实现如下。

scripts/index.js
import { promises as fsp } from "fs";
import path from "path";
import config from "./config.js";

async function getNote(dir, fileName) {
	// ...
}

async function readNotes(dir) {
	// ...
}

async function writeNote(note) {
	// ...
}

(async () => {
  const vaultNotes = await readNotes(config.vaultNotesPath);
  const astroNotes = await readNotes(config.astroNotesPath);

  const astroNoteFileNames = Object.keys(astroNotes);

  for (const vaultNoteFileName in vaultNotes) {
    if (!astroNoteFileNames.includes(vaultNoteFileName)) {
      // add note
      console.log(`Adding ${vaultNoteFileName}...`);
      await writeNote(vaultNotes[vaultNoteFileName]);
    } else {
      if (
        vaultNotes[vaultNoteFileName].content !==
        astroNotes[vaultNoteFileName].content
      ) {
        // update note
        console.log(`Updating ${vaultNoteFileName}...`);
        await writeNote(vaultNotes[vaultNoteFileName]);
      }
    }
  }

  for (const astroNoteFileName of astroNoteFileNames) {
    if (!Object.keys(vaultNotes).includes(astroNoteFileName)) {
      // remove note
      console.log(`Deleting ${astroNoteFileName}...`);
      await fsp.unlink(path.join(config.astroNotesPath, astroNoteFileName));
    }
  }
})();

这里简单说下思路。

首先,我们先来实现对新增笔记的处理。我需要分别读取 Obsidian vault 和 Astro site 各自目录下的条目,然后分别得到各自目录下的所有条目的文件名,然后遍历 vaultNotes 这边的文件名,如果遍历到的文件名在 astroNoteFileNames 里不存在,就说明这是一个新增的笔记,于是将这条笔记写入到 Astro site 里。

接着,我们再来实现对改动笔记的处理。一开始的处理还是一样,这次在遍历时,只要两边存在同样的文件名,就进一步判断内容是否有所改动,这个判断的标准也很简单,就是判断字符串是否相等,不一样的话,直接将这条笔记重新写入到 Astro site 里。

最后,要实现的是对删除笔记的处理,这个需求是一开始我没想到的。这个实现起来和新增笔记是一个相反的过程,遍历 astroNoteFileNames,如果遍历到的文件名在 vaultNoteFileNames 里不存在,就是说这条笔记被删除了,于是将这条笔记从 Astro site 里删除掉。

OK,大功告成。之后只要在终端里运行 node scripts/index.js 就可以自动同步内容了。而同步完之后,你就可以按你的方式来部署网站了。