Changes
9 changed files (+294/-3)
-
-
@@ -12,6 +12,7 @@ import { ofmHighlightFromMarkdown } from "./obsidian_markdown/mdast_util_ofm_highlight.ts";import { ofmHighlight } from "./obsidian_markdown/micromark_extension_ofm_highlight.ts"; import { ofmImageSize } from "./obsidian_markdown/mdast_util_ofm_image_size.ts"; import { macanaMarkAssets } from "./obsidian_markdown/mdast_util_macana_mark_assets.ts"; import { macanaMarkDocumentToken } from "./obsidian_markdown/mdast_util_macana_mark_document_token.ts"; import type { ContentParser,
-
@@ -21,6 +22,7 @@ } from "./interface.ts";import type { DocumentContent } from "../types.ts"; export { macanaReplaceAssetTokens } from "./obsidian_markdown/mdast_util_macana_replace_asset_tokens.ts"; export { macanaReplaceDocumentToken } from "./obsidian_markdown/mdast_util_macana_replace_document_tokens.ts"; function getFrontMatterValue( frontmatter: Record<string, unknown>,
-
@@ -66,7 +68,7 @@ }async function parseMarkdown( markdown: string | Uint8Array, { getAssetToken }: Pick< { getAssetToken, getDocumentToken }: Pick< ParseParameters, "getAssetToken" | "getDocumentToken" >,
-
@@ -79,6 +81,7 @@ofmImageSize(mdast); await macanaMarkAssets(mdast, getAssetToken); await macanaMarkDocumentToken(mdast, getDocumentToken); return mdast; }
-
-
-
@@ -0,0 +1,70 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import { assert, assertObjectMatch, } from "../../deps/deno.land/std/assert/mod.ts"; import type * as Mdast from "../../deps/esm.sh/mdast/types.ts"; import { fromMarkdown } from "../../deps/esm.sh/mdast-util-from-markdown/mod.ts"; import { macanaMarkDocumentToken } from "./mdast_util_macana_mark_document_token.ts"; const getDocumentToken = (path: readonly string[]) => `mxt_${path.join("/")}` as const; Deno.test("Should set Document Token on link", async () => { const mdast = fromMarkdown("[Foo](./foo.md)"); await macanaMarkDocumentToken(mdast, getDocumentToken); assertObjectMatch(mdast, { type: "root", children: [ { type: "paragraph", children: [ { type: "link", data: { macanaDocumentToken: "mxt_./foo.md", }, }, ], }, ], }); }); Deno.test("Should support absolute path", async () => { const mdast = fromMarkdown("[Foo](/foo.md)"); await macanaMarkDocumentToken(mdast, getDocumentToken); assertObjectMatch(mdast, { type: "root", children: [ { type: "paragraph", children: [ { type: "link", data: { macanaDocumentToken: "mxt_/foo.md", }, }, ], }, ], }); }); Deno.test("Should skip full image URL", async () => { const mdast = fromMarkdown("[Foo](https://example.com/foo.md)"); await macanaMarkDocumentToken(mdast, getDocumentToken); assert(!((mdast.children[0] as Mdast.Paragraph).children[0].data)); });
-
-
-
@@ -0,0 +1,86 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import type * as Mdast from "../../deps/esm.sh/mdast/types.ts"; import { SKIP, visit } from "../../deps/esm.sh/unist-util-visit/mod.ts"; import { definitions } from "../../deps/esm.sh/mdast-util-definitions/mod.ts"; import type { ParseParameters } from "../interface.ts"; import type { DocumentToken } from "../../types.ts"; const SEPARATOR = "/"; const IGNORE_REGEXP_PATTERN = /^([a-z0-9]+:\/\/|#)/i; function setDocumentToken(node: Mdast.Node, token: DocumentToken): void { node.data ??= {}; // @ts-expect-error: incorrect library type definition. node.data.macanaDocumentToken = token; } /** * Searches document references and Marks thoese node by setting `macanaDocumentToken` * with Document Token. * * This function mutates the Mdast tree in place. */ export async function macanaMarkDocumentToken( tree: Mdast.Nodes, getDocumentToken: ParseParameters["getDocumentToken"], ): Promise<void> { const promises: Promise<unknown>[] = []; const defs = definitions(tree); visit( tree, (node) => node.type === "link" || node.type === "linkReference", (node) => { switch (node.type) { case "link": { if (IGNORE_REGEXP_PATTERN.test(node.url)) { return SKIP; } const path = node.url.split(SEPARATOR); const token = getDocumentToken(path); if (token instanceof Promise) { promises.push(token.then((t) => { setDocumentToken(node, t); })); return SKIP; } setDocumentToken(node, token); return SKIP; } case "linkReference": { const def = defs(node.identifier); if (!def) { return; } if (IGNORE_REGEXP_PATTERN.test(def.url)) { return SKIP; } const path = def.url.split(SEPARATOR); const token = getDocumentToken(path); if (token instanceof Promise) { promises.push(token.then((t) => { setDocumentToken(node, t); })); return SKIP; } setDocumentToken(node, token); return SKIP; } } }, ); }
-
-
-
@@ -0,0 +1,43 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import { assertObjectMatch } from "../../deps/deno.land/std/assert/mod.ts"; import { fromMarkdown } from "../../deps/esm.sh/mdast-util-from-markdown/mod.ts"; import { macanaMarkDocumentToken } from "./mdast_util_macana_mark_document_token.ts"; import { macanaReplaceDocumentToken } from "./mdast_util_macana_replace_document_tokens.ts"; Deno.test("Should replace Document Token on links", async () => { const mdast = fromMarkdown("[Foo](./foo.png)"); await macanaMarkDocumentToken(mdast, () => { return "mxt_0"; }); await macanaReplaceDocumentToken(mdast, (token) => { if (token !== "mxt_0") { throw new Error("Unexpected token"); } return { path: "../FOO.MD", }; }); assertObjectMatch(mdast, { type: "root", children: [ { type: "paragraph", children: [ { type: "link", url: "../FOO.MD", }, ], }, ], }); });
-
-
-
@@ -0,0 +1,77 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import type * as Mdast from "../../deps/esm.sh/mdast/types.ts"; import { visit } from "../../deps/esm.sh/unist-util-visit/mod.ts"; import type { DocumentToken } from "../../types.ts"; function hasDocumentToken( node: Mdast.Node, ): node is typeof node & { data: { macanaDocumentToken: DocumentToken } } { return !!(node.data && ("macanaDocumentToken" in node.data) && typeof node.data.macanaDocumentToken === "string" && node.data.macanaDocumentToken.startsWith("mxt_")); } export interface ExchangeResult { /** * Path string appears on the final markup. */ path: string; } function replace(node: Mdast.Nodes, { path }: ExchangeResult): void { switch (node.type) { case "link": { node.url = path; return; } case "definition": { node.url = path; return; } } } /** * Modifies the given Mdast tree by searching nodes having `macanaDocumentToken` * property then replacing node properties. * This function modifies Mdast tree in place. * * @param tree - Mdast tree to modify. * @param exchange - A function that takes Document Token and returns properties required for constructing markup. */ export function macanaReplaceDocumentToken( tree: Mdast.Nodes, exchange: (token: DocumentToken) => ExchangeResult | Promise<ExchangeResult>, ): Promise<void> | void { const promises: Promise<unknown>[] = []; visit( tree, (node) => hasDocumentToken(node), (node) => { if (!hasDocumentToken(node)) { return; } const exchanged = exchange(node.data.macanaDocumentToken); if (exchanged instanceof Promise) { promises.push(exchanged.then((payload) => { replace(node, payload); })); return; } replace(node, exchanged); }, ); if (promises.length > 0) { return Promise.all(promises).then(() => {}); } }
-
-
-
@@ -5,7 +5,7 @@## Links - [Source code](https://github.com/pocka/macana) - [日本語版簡易ドキュメント](../ja/Overview.md) - [日本語版簡易ドキュメント](../ja/概要.md) ## Goals
-
-
ja/Overview.md (new)
-
-
@@ -9,6 +9,7 @@import type { BuildParameters, PageBuilder } from "../interface.ts"; import { macanaReplaceAssetTokens, macanaReplaceDocumentToken, type ObsidianMarkdownDocument, } from "../../content_parser/obsidian_markdown.ts"; import type { JSONCanvasDocument } from "../../content_parser/json_canvas.ts";
-
@@ -191,6 +192,17 @@ await file.read(),)); return toRelativePath(file.path, item.path); }, ); await macanaReplaceDocumentToken( item.content.content, async (token) => { const document = tree.exchangeToken(token); return { path: toRelativePath([...document.path, ""], item.path), }; }, ); }
-
-
-
@@ -161,7 +161,7 @@ return typeof token === "string" && token.startsWith("mxa_");} function isDocumentToken(token: unknown): token is DocumentToken { return typeof token === "string" && token.startsWith("mxd_"); return typeof token === "string" && token.startsWith("mxt_"); } function resolveFsrPath(
-