diff --git a/src/_utils/linked-articles.js b/src/_utils/linked-articles.js index a56b5a8..4174943 100644 --- a/src/_utils/linked-articles.js +++ b/src/_utils/linked-articles.js @@ -1,3 +1,12 @@ +const internalPostLink = /^\[([^\]]+)\]\(\/posts\/[^)\s]+\/?\)$/; + +const getLinkedArticleTitle = (value) => { + if (typeof value !== "string") return value; + + const match = value.match(internalPostLink); + return match ? match[1].trim() : value; +}; + export const validateLinkedArticles = (posts) => { const titles = new Set(posts.map((post) => post.data.title)); @@ -13,7 +22,9 @@ export const validateLinkedArticles = (posts) => { } const linkedTitleSet = new Set(); - for (const title of linkedArticles) { + for (const linkedArticle of linkedArticles) { + const title = getLinkedArticleTitle(linkedArticle); + if (typeof title !== "string" || title.trim() === "") { throw new Error( `Post '${post.inputPath}' must define 'linkedArticles' with non-empty article titles` @@ -44,5 +55,5 @@ export const resolveLinkedArticles = (posts, titles) => { if (!Array.isArray(posts) || !Array.isArray(titles)) return []; const postsByTitle = new Map(posts.map((post) => [post.data.title, post])); - return titles.map((title) => postsByTitle.get(title)).filter(Boolean); + return titles.map(getLinkedArticleTitle).map((title) => postsByTitle.get(title)).filter(Boolean); }; diff --git a/test/linked-articles.test.js b/test/linked-articles.test.js index 0423f64..b8138d5 100644 --- a/test/linked-articles.test.js +++ b/test/linked-articles.test.js @@ -24,6 +24,26 @@ test("Linked articles: accepts an article's list when every name identifies anot }); }); +test("Linked articles: accepts Obsidian's serialized internal Markdown links", () => { + const posts = [ + { + inputPath: "src/posts/project-crimson-week-7.md", + data: { + title: "Project Crimson Week 7", + linkedArticles: ["[Project Crimson Week 6](/posts/project-crimson-week-6/)"] + } + }, + { + inputPath: "src/posts/project-crimson-week-6.md", + url: "/posts/project-crimson-week-6/", + data: { title: "Project Crimson Week 6" } + } + ]; + + assert.doesNotThrow(() => validateLinkedArticles(posts)); + assert.deepEqual(resolveLinkedArticles(posts, posts[0].data.linkedArticles), [posts[1]]); +}); + test("Linked articles: rejects a non-list frontmatter value", () => { assert.throws( () => {