mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-27 23:04:05 -06:00
Obsidian Parsers (Wikilinks)
This commit is contained in:
parent
d9e0e0bffb
commit
169a570ed5
@ -2,6 +2,7 @@ import { QuartzTransformerPlugin } from "../../types"
|
||||
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||
import { SKIP } from "unist-util-visit"
|
||||
import { Root } from "mdast"
|
||||
import { PluggableList } from "unified"
|
||||
|
||||
interface Options {
|
||||
enabled: Boolean
|
||||
@ -24,11 +25,11 @@ const arrowMapping: Record<string, string> = {
|
||||
|
||||
const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/g)
|
||||
|
||||
export const ObsidianMarkdownArrow: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||
export const ObsidianArrow: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||
const opts: Options = { ...defaultOptions, ...userOpts }
|
||||
return {
|
||||
name: "ObsidianMarkdownArrow",
|
||||
markdownPlugins() {
|
||||
name: "ObsidianArrow",
|
||||
markdownPlugins(_ctx) {
|
||||
return [
|
||||
(tree: Root) => {
|
||||
if (opts.enabled) {
|
||||
@ -47,7 +48,7 @@ export const ObsidianMarkdownArrow: QuartzTransformerPlugin<Partial<Options>> =
|
||||
mdastFindReplace(tree, replacements)
|
||||
}
|
||||
},
|
||||
]
|
||||
] as PluggableList
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { QuartzTransformerPlugin } from "../../types"
|
||||
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||
import { Root } from "mdast"
|
||||
import { PluggableList } from "unified"
|
||||
|
||||
interface Options {
|
||||
enabled: Boolean
|
||||
@ -12,11 +13,11 @@ const defaultOptions: Options = {
|
||||
|
||||
const highlightRegex = new RegExp(/==([^=]+)==/g)
|
||||
|
||||
export const ObsidianMarkdownHighlights: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||
export const ObsidianHighlights: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||
const opts: Options = { ...defaultOptions, ...userOpts }
|
||||
return {
|
||||
name: "ObsidianMarkdownHighlights",
|
||||
markdownPlugins() {
|
||||
name: "ObsidianHighlights",
|
||||
markdownPlugins(ctx) {
|
||||
return [
|
||||
(tree: Root) => {
|
||||
if (opts.enabled) {
|
||||
@ -34,7 +35,7 @@ export const ObsidianMarkdownHighlights: QuartzTransformerPlugin<Partial<Options
|
||||
mdastFindReplace(tree, replacements)
|
||||
}
|
||||
},
|
||||
]
|
||||
] as PluggableList
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export { ObsidianMarkdownArrow } from "./arrows"
|
||||
export { ObsidianMarkdownHighlights } from "./highlights"
|
||||
export { ObsidianArrow } from "./arrows"
|
||||
export { ObsidianHighlights } from "./highlights"
|
||||
export { ObsidianWikilinks } from "./wikilinks"
|
||||
|
||||
@ -0,0 +1,164 @@
|
||||
import { QuartzTransformerPlugin } from "../../types"
|
||||
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||
import { FilePath, splitAnchor, slugifyFilePath } from "../../../util/path"
|
||||
import { Root } from "mdast"
|
||||
import { PluggableList } from "unified"
|
||||
|
||||
interface Options {
|
||||
enabled: Boolean
|
||||
}
|
||||
|
||||
const defaultOptions: Options = {
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const externalLinkRegex = /^https?:\/\//i
|
||||
|
||||
// ^\|([^\n])+\|\n(\|) -> matches the header row
|
||||
// ( ?:?-{3,}:? ?\|)+ -> matches the header row separator
|
||||
// (\|([^\n])+\|\n)+ -> matches the body rows
|
||||
const tableRegex = new RegExp(/^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/gm)
|
||||
|
||||
// matches any wikilink, only used for escaping wikilinks inside tables
|
||||
const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/g)
|
||||
|
||||
// !? -> optional embedding
|
||||
// \[\[ -> open brace
|
||||
// ([^\[\]\|\#]+) -> one or more non-special characters ([,],|, or #) (name)
|
||||
// (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
|
||||
// (\\?\|[^\[\]\#]+)? -> optional escape \ then | then one or more non-special characters (alias)
|
||||
const wikilinkRegex = new RegExp(
|
||||
/!?\[\[([^\[\]\|\#\\]+)?(#+[^\[\]\|\#\\]+)?(\\?\|[^\[\]\#]+)?\]\]/g,
|
||||
)
|
||||
|
||||
const wikilinkImageEmbedRegex = new RegExp(
|
||||
/^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/,
|
||||
)
|
||||
|
||||
export const ObsidianWikilinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||
const opts: Options = { ...defaultOptions, ...userOpts }
|
||||
return {
|
||||
name: "ObsidianWikilinks",
|
||||
textTransform(_ctx, src: string | Buffer) {
|
||||
if (src instanceof Buffer) {
|
||||
src = src.toString()
|
||||
}
|
||||
|
||||
// replace all wikilinks inside a table first
|
||||
src = src.replace(tableRegex, (value) => {
|
||||
// escape all aliases and headers in wikilinks inside a table
|
||||
return value.replace(tableWikilinkRegex, (_value, raw) => {
|
||||
// const [raw]: (string | undefined)[] = capture
|
||||
let escaped = raw ?? ""
|
||||
escaped = escaped.replace("#", "\\#")
|
||||
// escape pipe characters if they are not already escaped
|
||||
escaped = escaped.replace(/((^|[^\\])(\\\\)*)\|/g, "$1\\|")
|
||||
|
||||
return escaped
|
||||
})
|
||||
})
|
||||
|
||||
// replace all other wikilinks
|
||||
src = src.replace(wikilinkRegex, (value, ...capture) => {
|
||||
const [rawFp, rawHeader, rawAlias]: (string | undefined)[] = capture
|
||||
|
||||
const [fp, anchor] = splitAnchor(`${rawFp ?? ""}${rawHeader ?? ""}`)
|
||||
const blockRef = Boolean(rawHeader?.match(/^#?\^/)) ? "^" : ""
|
||||
const displayAnchor = anchor ? `#${blockRef}${anchor.trim().replace(/^#+/, "")}` : ""
|
||||
const displayAlias = rawAlias ?? rawHeader?.replace("#", "|") ?? ""
|
||||
const embedDisplay = value.startsWith("!") ? "!" : ""
|
||||
|
||||
if (rawFp?.match(externalLinkRegex)) {
|
||||
return `${embedDisplay}[${displayAlias.replace(/^\|/, "")}](${rawFp})`
|
||||
}
|
||||
|
||||
return `${embedDisplay}[[${fp}${displayAnchor}${displayAlias}]]`
|
||||
})
|
||||
|
||||
return src
|
||||
},
|
||||
markdownPlugins(_ctx) {
|
||||
return [
|
||||
(tree: Root, path) => {
|
||||
if (opts.enabled) {
|
||||
const replacements: [RegExp, string | ReplaceFunction][] = []
|
||||
replacements.push([
|
||||
wikilinkRegex,
|
||||
(value: string, ...capture: string[]) => {
|
||||
let [rawFp, rawHeader, rawAlias] = capture
|
||||
const fp = rawFp?.trim() ?? ""
|
||||
const anchor = rawHeader?.trim() ?? ""
|
||||
const alias = rawAlias?.slice(1).trim()
|
||||
|
||||
// embed cases
|
||||
if (value.startsWith("!")) {
|
||||
const ext: string = path.extname(fp).toLowerCase()
|
||||
const url = slugifyFilePath(fp as FilePath)
|
||||
if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg", ".webp"].includes(ext)) {
|
||||
const match = wikilinkImageEmbedRegex.exec(alias ?? "")
|
||||
const alt = match?.groups?.alt ?? ""
|
||||
const width = match?.groups?.width ?? "auto"
|
||||
const height = match?.groups?.height ?? "auto"
|
||||
return {
|
||||
type: "image",
|
||||
url,
|
||||
data: {
|
||||
hProperties: {
|
||||
width,
|
||||
height,
|
||||
alt,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else if ([".mp4", ".webm", ".ogv", ".mov", ".mkv"].includes(ext)) {
|
||||
return {
|
||||
type: "html",
|
||||
value: `<video src="${url}" controls></video>`,
|
||||
}
|
||||
} else if (
|
||||
[".mp3", ".webm", ".wav", ".m4a", ".ogg", ".3gp", ".flac"].includes(ext)
|
||||
) {
|
||||
return {
|
||||
type: "html",
|
||||
value: `<audio src="${url}" controls></audio>`,
|
||||
}
|
||||
} else if ([".pdf"].includes(ext)) {
|
||||
return {
|
||||
type: "html",
|
||||
value: `<iframe src="${url}" class="pdf"></iframe>`,
|
||||
}
|
||||
} else {
|
||||
const block = anchor
|
||||
return {
|
||||
type: "html",
|
||||
data: { hProperties: { transclude: true } },
|
||||
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}" data-embed-alias="${alias}"><a href="${
|
||||
url + anchor
|
||||
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, fall through to regular link
|
||||
}
|
||||
|
||||
// internal link
|
||||
const url = fp + anchor
|
||||
return {
|
||||
type: "link",
|
||||
url,
|
||||
children: [
|
||||
{
|
||||
type: "text",
|
||||
value: alias ?? fp,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
])
|
||||
mdastFindReplace(tree, replacements)
|
||||
}
|
||||
},
|
||||
] as PluggableList
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,7 @@ import smartypants from "remark-smartypants"
|
||||
import rehypeSlug from "rehype-slug"
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||
|
||||
import { ObsidianMarkdownArrow, ObsidianMarkdownHighlights } from "../parsers/obsidian"
|
||||
import { ObsidianArrow, ObsidianHighlights, ObsidianWikilinks } from "../parsers/obsidian"
|
||||
|
||||
export interface CommonMarkOptions {
|
||||
option1: Boolean
|
||||
@ -172,10 +172,12 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<ObsidianO
|
||||
const opts = { ...defaultObsidianOptions, ...userOpts }
|
||||
return {
|
||||
name: "ObsidianFlavoredMarkdown",
|
||||
textTransform(_ctx, src) {
|
||||
textTransform(ctx, src) {
|
||||
ObsidianWikilinks({ enabled: opts.wikilinks }).textTransform!(ctx, src)
|
||||
|
||||
return src
|
||||
},
|
||||
markdownPlugins(_ctx) {
|
||||
markdownPlugins(ctx) {
|
||||
const plugins: PluggableList = []
|
||||
|
||||
plugins.push(() => {
|
||||
@ -183,9 +185,11 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<ObsidianO
|
||||
//const replacements: [RegExp, string | ReplaceFunction][] = []
|
||||
//const base = pathToRoot(file.data.slug!)
|
||||
|
||||
ObsidianMarkdownHighlights({ enabled: opts.highlight })
|
||||
ObsidianWikilinks({ enabled: opts.wikilinks }).markdownPlugins!(ctx)
|
||||
|
||||
ObsidianMarkdownArrow({ enabled: opts.parseArrows })
|
||||
ObsidianHighlights({ enabled: opts.highlight }).markdownPlugins!(ctx)
|
||||
|
||||
ObsidianArrow({ enabled: opts.parseArrows }).markdownPlugins!(ctx)
|
||||
|
||||
//mdastFindReplace(tree, replacements)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user