Skip to content

Instantly share code, notes, and snippets.

@lelesrc
Created July 22, 2020 20:31
Show Gist options
  • Save lelesrc/54fb5a1d7b22668543ace2764173992a to your computer and use it in GitHub Desktop.
Save lelesrc/54fb5a1d7b22668543ace2764173992a to your computer and use it in GitHub Desktop.
// foam-obsidian-importer.js
// Import from obsidian to foam
//
// • Copy all files recursively from SRC_DIR to DST_DIR.
// • For each .md file:
// - rename to kebab-case.md
// - add header "# filename /n/n"
// - fix internal links and attachments links
// • Copy any other (non .md) file.
//
// Obsidian attachments will be imported from ATTACHMENTS_DIR
//
// Usage:
// 1. npm install fs-extra slugify prepend-file
// 2. edit SRC_DIR, DST_DIR, ATTACHMENTS_DIR
// 3. node foam-obsidian-importer.js
// SRC_DIR (obsdian); DST_DIR (foam)
const SRC_DIR = "/projects/obsidian";
const DST_DIR = "/projects/foam/imported";
// folder where obsidian attachments are stored, relative to SRC_DIR
const ATTACHMENTS_DIR = "attachments/"
const path = require("path");
const fs = require("fs-extra");
const slugify = require("slugify");
const prependFile = require("prepend-file");
// get files in dir
async function getFiles(dir) {
const dirents = await fs.promises.readdir(dir, {
withFileTypes: true,
});
const files = await Promise.all(
dirents.map((dirent) => {
const res = path.resolve(dir, dirent.name);
return dirent.isDirectory() ? getFiles(res) : res;
})
);
return Array.prototype.concat(...files);
}
// fix internal links for file and write to dst_path
function fix_internal_links(file, dst_path) {
// read file
var data = fs.readFileSync(file, "utf-8");
// fix internal links
var re_internal_links = /\[\[(.*)\]\]/gm
data = data.replace(re_internal_links,
(match, p1) => {
return '[[' + slugify(p1.toLowerCase()) + ']]'
}
)
// fix file links
var re_file_links = /\[\[((.*)\.(png|jpg|gif|svg))\]\]/gm
data = data.replace(re_file_links,
(match, p1, p2, p3) => {
let rlt_path = path.relative(SRC_DIR, path.parse(file).dir);
let rlt_depth = (rlt_path.match(/\//g) || []).length + 1
return '[' + p2 + '](' + '../'.repeat(rlt_depth) + ATTACHMENTS_DIR + slugify(p1.toLowerCase()) + ')'
}
)
// save file
fs.writeFileSync(dst_path, data, "utf-8");
}
// do the magic
function process(file) {
let current_file = path.parse(file);
let file_slug = slugify(current_file.name.toLowerCase());
let rlt_path = path.relative(SRC_DIR, current_file.dir);
var dst_path = path.resolve(DST_DIR, rlt_path, file_slug + current_file.ext);
let dst_file = path.parse(
path.resolve(DST_DIR, rlt_path, file_slug + current_file.ext)
);
// create dst dir if not exists
fs.ensureDirSync(dst_file.dir);
// if markdown
if (current_file.ext == ".md") {
// prepend header "# <Filename> \n\n"
prependFile(
dst_path,
"# " +
current_file.name.charAt(0).toUpperCase() +
current_file.name.slice(1) +
"\n\n",
(err) => {
if (err) {
console.log(err);
}
}
);
// fix relative links
fix_internal_links(file, dst_path)
// if any other file extension
} else {
// copy file to dst dir
fs.copySync(file, dst_path);
}
}
function main() {
getFiles(SRC_DIR)
.then((files) =>
files.forEach((src_file) => {
process(src_file);
})
)
.catch((e) => console.error(e));
}
main();
@jaltgen
Copy link

jaltgen commented Dec 24, 2023

Hey thanks for this, super neat! I'm just starting to think to modify this to work with nb style linking, which requires the relative path to the notebook root. This probably requires:

  • changing filenames first, building a dir with old and new filenames, and path, in the process
  • when replacing the links, cross-referencing the old link with the old file-name and supplementing the known relative path in front of the new slug

Sadly, my JS skills are beyond rusty. Ah well - a holiday project.

Thanks again for the cool work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment