mirror of
				https://github.com/jackyzha0/quartz.git
				synced 2025-10-30 03:27:41 +01:00 
			
		
		
		
	feat: resolve block references in obsidian markdown
This commit is contained in:
		
							parent
							
								
									e3b879741b
								
							
						
					
					
						commit
						6ecdcb5e24
					
				| @ -1,6 +1,7 @@ | ||||
| import { PluggableList } from "unified" | ||||
| import { QuartzTransformerPlugin } from "../types" | ||||
| import { Root, HTML, BlockContent, DefinitionContent, Code, Paragraph } from "mdast" | ||||
| import { Element, Literal } from 'hast' | ||||
| import { Replace, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace" | ||||
| import { slug as slugAnchor } from "github-slugger" | ||||
| import rehypeRaw from "rehype-raw" | ||||
| @ -21,6 +22,7 @@ export interface Options { | ||||
|   callouts: boolean | ||||
|   mermaid: boolean | ||||
|   parseTags: boolean | ||||
|   parseBlockReferences: boolean | ||||
|   enableInHtmlEmbed: boolean | ||||
| } | ||||
| 
 | ||||
| @ -31,6 +33,7 @@ const defaultOptions: Options = { | ||||
|   callouts: true, | ||||
|   mermaid: true, | ||||
|   parseTags: true, | ||||
|   parseBlockReferences: true, | ||||
|   enableInHtmlEmbed: false, | ||||
| } | ||||
| 
 | ||||
| @ -121,6 +124,7 @@ const calloutLineRegex = new RegExp(/^> *\[\!\w+\][+-]?.*$/, "gm") | ||||
| // (?:[-_\p{L}])+       -> non-capturing group, non-empty string of (Unicode-aware) alpha-numeric characters, hyphens and/or underscores
 | ||||
| // (?:\/[-_\p{L}]+)*)   -> non-capturing group, matches an arbitrary number of tag strings separated by "/"
 | ||||
| const tagRegex = new RegExp(/(?:^| )#((?:[-_\p{L}\d])+(?:\/[-_\p{L}\d]+)*)/, "gu") | ||||
| const blockReferenceRegex = new RegExp(/\^([A-Za-z0-9]+)$/, "g") | ||||
| 
 | ||||
| export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = ( | ||||
|   userOpts, | ||||
| @ -133,29 +137,29 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | ||||
|   } | ||||
|   const findAndReplace = opts.enableInHtmlEmbed | ||||
|     ? (tree: Root, regex: RegExp, replace?: Replace | null | undefined) => { | ||||
|         if (replace) { | ||||
|           visit(tree, "html", (node: HTML) => { | ||||
|             if (typeof replace === "string") { | ||||
|               node.value = node.value.replace(regex, replace) | ||||
|             } else { | ||||
|               node.value = node.value.replaceAll(regex, (substring: string, ...args) => { | ||||
|                 const replaceValue = replace(substring, ...args) | ||||
|                 if (typeof replaceValue === "string") { | ||||
|                   return replaceValue | ||||
|                 } else if (Array.isArray(replaceValue)) { | ||||
|                   return replaceValue.map(mdastToHtml).join("") | ||||
|                 } else if (typeof replaceValue === "object" && replaceValue !== null) { | ||||
|                   return mdastToHtml(replaceValue) | ||||
|                 } else { | ||||
|                   return substring | ||||
|                 } | ||||
|               }) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
| 
 | ||||
|         mdastFindReplace(tree, regex, replace) | ||||
|       if (replace) { | ||||
|         visit(tree, "html", (node: HTML) => { | ||||
|           if (typeof replace === "string") { | ||||
|             node.value = node.value.replace(regex, replace) | ||||
|           } else { | ||||
|             node.value = node.value.replaceAll(regex, (substring: string, ...args) => { | ||||
|               const replaceValue = replace(substring, ...args) | ||||
|               if (typeof replaceValue === "string") { | ||||
|                 return replaceValue | ||||
|               } else if (Array.isArray(replaceValue)) { | ||||
|                 return replaceValue.map(mdastToHtml).join("") | ||||
|               } else if (typeof replaceValue === "object" && replaceValue !== null) { | ||||
|                 return mdastToHtml(replaceValue) | ||||
|               } else { | ||||
|                 return substring | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       mdastFindReplace(tree, regex, replace) | ||||
|     } | ||||
|     : mdastFindReplace | ||||
| 
 | ||||
|   return { | ||||
| @ -353,9 +357,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | ||||
|                 node.data = { | ||||
|                   hProperties: { | ||||
|                     ...(node.data?.hProperties ?? {}), | ||||
|                     className: `callout ${collapse ? "is-collapsible" : ""} ${ | ||||
|                       defaultState === "collapsed" ? "is-collapsed" : "" | ||||
|                     }`,
 | ||||
|                     className: `callout ${collapse ? "is-collapsible" : ""} ${defaultState === "collapsed" ? "is-collapsed" : "" | ||||
|                       }`,
 | ||||
|                     "data-callout": calloutType, | ||||
|                     "data-callout-fold": collapse, | ||||
|                   }, | ||||
| @ -411,11 +414,38 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       return plugins | ||||
|     }, | ||||
|     htmlPlugins() { | ||||
|       return [rehypeRaw] | ||||
|       const plugins = [rehypeRaw] | ||||
| 
 | ||||
|       if (opts.parseBlockReferences) { | ||||
|         plugins.push(() => { | ||||
|           return (tree, file) => { | ||||
|             file.data.blocks = {} | ||||
|             const validTagTypes = new Set(["blockquote", "p", "li"]) | ||||
|             visit(tree, "element", (node, _index, _parent) => { | ||||
|               if (validTagTypes.has(node.tagName)) { | ||||
|                 const last = node.children.at(-1) as Literal | ||||
|                 if (last.value && typeof last.value === 'string') { | ||||
|                   const matches = last.value.match(blockReferenceRegex) | ||||
|                   if (matches && matches.length >= 1) { | ||||
|                     last.value = last.value.slice(0, -matches[0].length) | ||||
|                     const block = matches[0].slice(1) | ||||
|                     node.properties = { | ||||
|                       ...node.properties, | ||||
|                       id: block | ||||
|                     } | ||||
|                     file.data.blocks![block] = node | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       return plugins | ||||
|     }, | ||||
|     externalResources() { | ||||
|       const js: JSResource[] = [] | ||||
| @ -454,3 +484,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| declare module "vfile" { | ||||
|   interface DataMap { | ||||
|     blocks: Record<string, Element> | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Jacky Zhao
						Jacky Zhao