const fs = require("fs"); const path = require("path"); const util = require('node:util'); const exec = util.promisify(require('node:child_process').exec); const { Feed } = require('feed'); function walk(dir, cb) { const list = fs.readdirSync(dir); return Promise.all(list.map(file => { const resolvedFile = path.resolve(dir, file); const stat = fs.statSync(resolvedFile); if (stat.isDirectory()) { return walk(resolvedFile, cb); } else { return new Promise((resolve) => cb(dir, resolvedFile, resolve)); } })); } function toSlug(string) { return string.toLowerCase().replaceAll(' ', '-'); } (async () => { const blockRefs = {}; const blockLinks = {}; const indices = []; await walk("./garden-output/logseq-pages", (dir, file, resolve) => { const filePath = path.resolve(dir, file); const data = fs.readFileSync(filePath).toString(); const slug = path.basename(file, ".md").replaceAll('___', '/').replaceAll(/%3F/gi, '').replace('what-is-content-', 'what-is-content'); for (const match of data.matchAll(/(.*)\n\s*id:: (.*)/gm)) { const text = match[1]; const id = match[2]; const link = `/garden/${slug}/index.md#${id}`; blockLinks[id] = link; blockRefs[id] = `[${text}](${link})`; } if (data.match(/index: "true"/g)) { indices.push(slug); } resolve(); }); const pageLinks = {}; const taggedBy = {}; const tagged = {}; const referencedBy = {}; // Walk through the pages to make sure we get the canonical name page (pre-slug) // The logseq-export README made it sound like even the title property is transformed sometimes await walk("./Garden/pages", (dir, file, resolve) => { const filePath = path.resolve(dir, file); const data = fs.readFileSync(filePath).toString(); if (!data.match(/public::/g)) { resolve(); return; } const name = path.basename(file, ".md").replaceAll('___', '/'); const slug = toSlug(name).replaceAll(/%3F/gi, '').replaceAll('\'', '-'); const link = `/garden/${slug}/index.md`; pageLinks[name.replaceAll(/%3F/gi, '?')] = link; for (const match of data.matchAll(/alias:: (.*)/g)) { match[1].split(", ").forEach(page => (pageLinks[page] = link)); } for (const match of data.matchAll(/tags:: (.*)/g)) { match[1].split(", ").forEach(page => { const pageSlug = toSlug(page); taggedBy[pageSlug] = [...(taggedBy[pageSlug] ?? []), name]; tagged[slug] = [...(tagged[slug] ?? []), page]; }); } if (!indices.includes(slug)) { for (const match of data.matchAll(/\[\[([^\[\]]*)\]\]/g)) { const pageSlug = toSlug(match[1]); referencedBy[pageSlug] = [...(referencedBy[pageSlug] ?? []), name]; } } resolve(); }); Object.keys(referencedBy).forEach(page => { referencedBy[page] = Array.from(new Set(referencedBy[page])); }); await walk("./garden-output/logseq-pages", (dir, file, resolve) => { const filePath = path.resolve(dir, file); let data = fs.readFileSync(filePath).toString(); // Replace youtube embeds data = data.replaceAll( /{{video https:\/\/(?:www\.)?youtube\.com\/watch\?v=(.*)}}/g, '