@global-torque/markdown-it-wikilinks
v0.2.0
Published
Framework-free markdown-it wikilinks with optional contained Node tooltip resolution.
Downloads
350
Maintainers
Readme
@global-torque/markdown-it-wikilinks
Public release:
0.2.0. Install the versioned package from npm.
An ESM-only markdown-it plugin for escaped Obsidian-style [[wikilinks]]. The
root package produces ordinary <a> HTML and has no Node filesystem, Vue, or
VitePress dependency. Filesystem-backed frontmatter lookup is isolated in the
explicit ./node adapter.
Install
pnpm add @global-torque/[email protected] [email protected]Node 22 or newer and markdown-it 14 are supported.
Core usage
import MarkdownIt from "markdown-it";
import wikilinks from "@global-torque/markdown-it-wikilinks";
const markdown = new MarkdownIt().use(
wikilinks({
uriSuffix: "",
postProcessPagePath: (pagePath) => pagePath.toLowerCase(),
}),
);
markdown.renderInline("[[guides/Getting Started|Read the guide]]");
// <a href="./guides/getting started" title="Read the guide">Read the guide</a>The renderer supports labels, headings, queries, current-page anchors,
Unicode, and existing percent escapes. http:, https:, mailto:, and tel:
destinations pass through. Other schemes are neutralized. Relative paths stay
relative; absolute paths and makeAllLinksAbsolute use baseURL; synthetic
/./ segments are never emitted.
This example is extracted from the packed README and executed in clean npm and pnpm consumers:
import assert from "node:assert/strict";
import { createWikilinkHref } from "@global-torque/markdown-it-wikilinks/url";
assert.equal(
createWikilinkHref("guides/Getting Started?mode=full#Read Me", {
uriSuffix: "",
}),
"./guides/Getting_Started?mode=full#Read_Me",
);
assert.equal(createWikilinkHref("javascript:alert(1)"), "#");Available core options are:
baseURL,relativeBaseURL,makeAllLinksAbsolute, anduriSuffix;- immutable, safe-listed
htmlAttributes(standard inert anchor attributes plusaria-*anddata-*); generatePagePathFromLabel,postProcessPagePath,postProcessPageHash, andpostProcessLabel;- synchronous
resolveHref,resolveTooltip, andrenderTooltipcallbacks.
Without renderTooltip, resolved tooltip text becomes a standard escaped
title attribute. Framework markup is explicitly host-owned:
const options = {
resolveTooltip: ({ target }) => tooltipByTarget.get(target),
renderTooltip: ({ anchorHtml, escapedTooltip }) =>
`<AppTooltip>${anchorHtml}<span>${escapedTooltip}</span></AppTooltip>`,
};renderTooltip is a trusted integration boundary. Insert escapedTooltip,
not raw tooltip, unless the host performs its own equivalent escaping.
Contained Node Resolver
import { createFrontmatterTooltipResolver } from "@global-torque/markdown-it-wikilinks/node";
const resolver = createFrontmatterTooltipResolver({
root: new URL("./docs", import.meta.url).pathname,
fields: ["summary", "description"],
excludeDirectories: ["public"],
});
const markdown = new MarkdownIt().use(
wikilinks({ resolveTooltip: resolver.resolveTooltip }),
);
resolver.refresh();
resolver.dispose();The resolver uses gray-matter, atomically parses and caches tooltip values
during construction/refresh(), resolves explicit and same-folder paths before
unique basenames, returns undefined for missing content, and throws on
ambiguous or malformed content. It never reads a path during link rendering.
Symlinks are rejected by default. Opted-in symlinks are indexed under their
lexical alias only when their real paths remain inside the declared real root.
Exports
@global-torque/markdown-it-wikilinks: core plugin and callback contracts;@global-torque/markdown-it-wikilinks/url: pure href builder;@global-torque/markdown-it-wikilinks/node: contained frontmatter resolver.
Generated API references are in docs/api and
docs/api-node. Committed API reports are in etc/.
Migration From 0.1
- Replace
generatePageNameFromLabelwithgeneratePagePathFromLabel. - Replace
postProcessPageNamewithpostProcessPagePath. - Remove imports of the mutable
Urlclass; usecreateWikilinkHrefwhen a standalone href builder is required. - Replace root
docsRootandtooltipFrontmatterFieldoptions with acreateFrontmatterTooltipResolver()instance passed throughresolveTooltip. - Move Vue or other framework wrappers into
renderTooltipin the consuming application. - Review URL snapshots: nested relative destinations change from invalid
/./pathoutput to./path.
Rollback by pinning the last reviewed artifact digest, reverting the consumer callback migration, and recording the rejected beta. Never replace an existing beta tarball or tag with different bytes.
Security And Release State
The core escapes hrefs, labels, titles, and static attributes, ignores
attributes outside its inert anchor safe list, and revalidates both default and
host-resolved href schemes. Caller-supplied href, event handlers, style, and
other active attributes are ignored. The Node adapter treats content paths and
symlinks as untrusted. Application tooltip render callbacks remain trusted
code.
See SECURITY.md for supported releases and private vulnerability reporting.
