From a61575d1713e17e9c12f1cd3b1502feec4f02dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Ko=C5=9Bcielniak?= Date: Fri, 21 Jul 2023 12:02:59 +0200 Subject: [PATCH 1/6] feat: Add logic for backlinks (WIP) --- .vitepress/config.ts | 15 +- .vitepress/plugins/backlinks/backlinks.ts | 75 +++ .vitepress/theme/LayoutWithTitle.vue | 20 +- .vitepress/theme/index.ts | 6 +- .vitepress/theme/lay2.ts | 22 + .../theme/plugins/zettelkasten/backlinks.ts | 16 + .../theme/plugins/zettelkasten/collection.ts | 7 + .../plugins/zettelkasten/components/index.ts | 1 + .../zettelkasten/components/references.tsx | 48 ++ .../theme/plugins/zettelkasten/index.scss | 83 +++ .../theme/plugins/zettelkasten/index.ts | 2 + .../zettelkasten/markdownItBacklinks.ts | 52 ++ package.json | 3 + todo.md | 1 + vite.config.ts | 6 + yarn.lock | 545 +++++++++++++++++- 16 files changed, 894 insertions(+), 8 deletions(-) create mode 100644 .vitepress/plugins/backlinks/backlinks.ts create mode 100644 .vitepress/theme/lay2.ts create mode 100644 .vitepress/theme/plugins/zettelkasten/backlinks.ts create mode 100644 .vitepress/theme/plugins/zettelkasten/collection.ts create mode 100644 .vitepress/theme/plugins/zettelkasten/components/index.ts create mode 100644 .vitepress/theme/plugins/zettelkasten/components/references.tsx create mode 100644 .vitepress/theme/plugins/zettelkasten/index.scss create mode 100644 .vitepress/theme/plugins/zettelkasten/index.ts create mode 100644 .vitepress/theme/plugins/zettelkasten/markdownItBacklinks.ts create mode 100644 todo.md create mode 100644 vite.config.ts diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 4f5ea77..943221e 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -3,13 +3,15 @@ import mdWikilinks from "markdown-it-wikilinks"; import mdCheckbox from "markdown-it-checkbox"; import mdInclude from "markdown-it-include"; import { sidebar } from "./plugins/sidebar"; +import { getBacklinks } from "./theme/plugins/zettelkasten/backlinks"; +import { markdownItBacklinks } from "./theme/plugins/zettelkasten"; export default defineConfig({ base: "/", title: "🥦 kościelniak.pro", description: "Things I know", ignoreDeadLinks: true, - srcExclude: ["private"], + srcExclude: ["private", "knowledge", "about", "reading"], themeConfig: { docFooter: { next: false, @@ -34,7 +36,10 @@ export default defineConfig({ postProcessLabel: (label) => label.split("/").pop(), }); - md.use(wikilinks).use(mdCheckbox).use(mdInclude, "partials"); + md.use(wikilinks) + .use(mdCheckbox) + .use(mdInclude, "partials") + .use(markdownItBacklinks, { vault: "/" }); }, }, head: [ @@ -73,4 +78,10 @@ export default defineConfig({ ], ["meta", { name: "msapplication-TileColor", content: "#3a0839" }], ], + transformPageData: async (pageData) => { + let backlinks = await getBacklinks(pageData); + return { + backlinks, + }; + }, }); diff --git a/.vitepress/plugins/backlinks/backlinks.ts b/.vitepress/plugins/backlinks/backlinks.ts new file mode 100644 index 0000000..ccc4e86 --- /dev/null +++ b/.vitepress/plugins/backlinks/backlinks.ts @@ -0,0 +1,75 @@ +import type { PluginWithParams } from "markdown-it"; +import { PageData } from "vitepress"; + +const wikilinksRegexp = /\[{2}\s*(.+?)\s*\]{2}/gi; + +export interface BacklinkPageInfo { + title: string; + path: string; +} + +export const collection: Map = new Map(); + +const linkMatcher = (cap: RegExpExecArray) => { + const backlink = cap[1].split("|"); + const path = backlink[0]; + const title = backlink[backlink.length - 1]; + + return { path, title }; +}; + +const backlinks: PluginWithParams = (md): void => { + md.core.ruler.before("normalize", "bls", (state) => { + const relativePath = state.env.relativePath?.replace(".md", ""); + const selfTitle = state.env.frontmatter?.title; + + if (!state.env.frontmatter) { + return; + } + + state.env.frontmatter.yourCustomData = + "Hello, this is custom data from your plugin!"; + + ((src) => { + let cap: RegExpExecArray | null; + + while ((cap = wikilinksRegexp.exec(src))) { + if (!selfTitle || !relativePath) { + continue; + } + + const { title, path } = linkMatcher(cap); + + const backlinks = collection.get(path) ?? []; + + let found = false; + + for (const backlink of backlinks) { + if (backlink.path == relativePath) { + found = true; + break; + } + } + + if (!found) { + backlinks.push({ + title: selfTitle, + path: relativePath, + }); + } + collection.set(path, backlinks); + } + })(state.src); + }); +}; + +export default backlinks; + +export const getBacklinks = async (pageData: PageData) => { + const relativePath = `${pageData.relativePath.replace(".md", "")}`; + + if (!collection.has(relativePath)) { + return []; + } + return collection.get(relativePath); +}; diff --git a/.vitepress/theme/LayoutWithTitle.vue b/.vitepress/theme/LayoutWithTitle.vue index 686d528..ff3d064 100644 --- a/.vitepress/theme/LayoutWithTitle.vue +++ b/.vitepress/theme/LayoutWithTitle.vue @@ -1,19 +1,35 @@ diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index bd86153..99196ac 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -2,9 +2,11 @@ import Theme from "vitepress/theme"; import Title from "./LayoutWithTitle.vue"; import "./style.css"; +import { Layout } from "./lay2"; export default { - Layout: Title, extends: Theme, - enhanceApp({ app, router, siteData }) {}, + Layout: Layout, + // enhanceApp({ app, router, siteData }) {}, + // TODO: Moze z tego skorzystac jakos? }; diff --git a/.vitepress/theme/lay2.ts b/.vitepress/theme/lay2.ts new file mode 100644 index 0000000..d5886ad --- /dev/null +++ b/.vitepress/theme/lay2.ts @@ -0,0 +1,22 @@ +import { defineComponent, h, reactive, watch } from "vue"; +import DefaultTheme from "vitepress/theme"; +import { BacklinkReferences } from "./plugins/zettelkasten/components"; +import { useData, useRoute } from "vitepress"; + +export const Layout = defineComponent({ + name: "Layout", + + setup() { + const route = useRoute(); + const state = reactive({ key: route.path }); + watch( + () => route.path, + (path) => (state.key = path), + ); + + return () => + h(DefaultTheme.Layout, null, { + "doc-before": () => h(BacklinkReferences, { key: `${state.key}` }), + }); + }, +}); diff --git a/.vitepress/theme/plugins/zettelkasten/backlinks.ts b/.vitepress/theme/plugins/zettelkasten/backlinks.ts new file mode 100644 index 0000000..0a3a0d4 --- /dev/null +++ b/.vitepress/theme/plugins/zettelkasten/backlinks.ts @@ -0,0 +1,16 @@ +import { collection } from "./collection"; +import type { PageData } from "vitepress"; + +// NOTE: collection here is lazy collected during the dev runtime. +// it won't generate the backlink references information until you visit the +// page. +export const getBacklinks = async (pageData: PageData) => { + const relativePath = `${pageData.relativePath.replace(".md", "")}`; + + console.log({ relativePath }, collection); + + if (!collection.has(relativePath)) { + return []; + } + return collection.get(relativePath); +}; diff --git a/.vitepress/theme/plugins/zettelkasten/collection.ts b/.vitepress/theme/plugins/zettelkasten/collection.ts new file mode 100644 index 0000000..3a2a42a --- /dev/null +++ b/.vitepress/theme/plugins/zettelkasten/collection.ts @@ -0,0 +1,7 @@ +export interface BacklinkPageInfo { + title: string, + path: string, + content: string +} + +export const collection: Map = new Map() diff --git a/.vitepress/theme/plugins/zettelkasten/components/index.ts b/.vitepress/theme/plugins/zettelkasten/components/index.ts new file mode 100644 index 0000000..e9b3bb6 --- /dev/null +++ b/.vitepress/theme/plugins/zettelkasten/components/index.ts @@ -0,0 +1 @@ +export * from './references' diff --git a/.vitepress/theme/plugins/zettelkasten/components/references.tsx b/.vitepress/theme/plugins/zettelkasten/components/references.tsx new file mode 100644 index 0000000..a1bb0ab --- /dev/null +++ b/.vitepress/theme/plugins/zettelkasten/components/references.tsx @@ -0,0 +1,48 @@ +import { defineComponent } from "vue"; +import { useData, useRoute, useRouter } from "vitepress"; + +export const BacklinkReferences = defineComponent({ + name: "BacklinkReferences", + + setup() { + console.log("works?"); + + const { page } = useData(); + const allBacklinks = page.value["backlinks"]; // (); + console.log(allBacklinks); + let r = useRouter(); + let route = useRoute(); + let backlinks = []; + for (const backlink of allBacklinks) { + if (`/${backlink.path}` == route.path) { + // exclude self reference + continue; + } + backlinks.push(backlink); + } + + // TODO: Something's odd about that collection for the fuck sake + + return () => ( +