Line data Source code
1 : /** 2 : * INFO: Tools to manipulate and render Markdown 3 : */ 4 : 5 1 : import {expandGlobSync, WalkEntry} from "https://deno.land/std/fs/mod.ts"; 6 1 : import {Remarkable} from '../external/remarkable.js'; 7 1 : import meta from '../external/remarkable-meta.js'; 8 1 : import wikilinks from '../external/remarkable-wikilink.js'; 9 1 : import {remarkablePluginHeadingId} from '../external/remarkable-plugin-heading-id.js'; 10 1 : import hljs from '../external/highlight.js'; 11 : 12 1 : let md = new Remarkable({ 13 1 : html: true, 14 0 : highlight: function (str: any, lang: any) { 15 0 : if (lang && hljs.getLanguage(lang)) { 16 0 : try { 17 0 : return hljs.highlight(lang, str).value; 18 0 : } catch (err) { 19 0 : } 20 0 : } 21 : 22 0 : try { 23 0 : return hljs.highlightAuto(str).value; 24 0 : } catch (err) { 25 0 : } 26 : 27 0 : return ''; // use external default escaping 28 0 : } 29 1 : }); 30 1 : md.use(meta); 31 1 : md.use(wikilinks) 32 1 : md.use(remarkablePluginHeadingId, { 33 : // this option is for create id, optional option, default value is (level, content, idx) => `${content}` 34 0 : createId: ( 35 0 : level: 1 | 2 | 3 | 4 | 5 | 6, 36 0 : content: string, 37 0 : idx: number 38 0 : ) => normaliseLink(content) 39 1 : }) 40 1 : md.inline.ruler.disable([ 41 1 : 'escape' 42 1 : ]); 43 : 44 0 : export function createMarkdownTOC(content: string) { 45 0 : return content.split("\n") 46 0 : .filter(x => x.startsWith("##")) 47 0 : .map(x => x.substring(1)) 48 0 : .map(x => { 49 0 : let parts = x.split("# ") 50 0 : return parts[0] + '# ' + `<a href="%${normaliseLink(parts[1] == undefined ? "" : parts[1])}">${parts[1]}</a>` 51 0 : }).map(x => x.replaceAll("# ", "- ").replaceAll("#", "\t").replaceAll("%", "#")) 52 0 : .join("\n") 53 : // 54 0 : } 55 : 56 0 : export function renderMarkdownSnippetAsHTML(snippet: string) { 57 0 : let renderer = new Remarkable({"html": true}); 58 0 : return md.render(snippet) 59 0 : } 60 : 61 1 : abstract class SourceFile { 62 : protected _content: string; 63 : 64 : 65 1 : constructor(content: string) { 66 4 : this._content = content; 67 1 : } 68 : 69 : abstract renderToHTML(): string; 70 1 : } 71 : 72 1 : export class MarkdownFile extends SourceFile { 73 0 : get mTime(): Date { 74 0 : return this._mTime; 75 0 : } 76 0 : public get backlinks(): Set<string> { 77 0 : return this._backlinks; 78 0 : } 79 : 80 0 : public get title(): string { 81 0 : return this._title; 82 0 : } 83 : 84 0 : public get content(): string { 85 0 : return this._content; 86 0 : } 87 : 88 0 : public set content(value: string) { 89 0 : this._content = value; 90 0 : } 91 : 92 : private readonly _title: string 93 4 : private _backlinks: Set<string> = new Set(); 94 : private readonly _mTime: Date 95 : 96 1 : constructor(title: string, content: string, mTime: Date) { 97 4 : super(content); 98 4 : this._title = title 99 4 : this._mTime = mTime 100 1 : } 101 : 102 0 : renderToHTML(): string { 103 0 : const env = {frontMatter: undefined}; 104 0 : const result = md.render(this.content, env); 105 : // console.log(env); 106 0 : return result; 107 0 : } 108 1 : } 109 : 110 4 : function globFiles(path: string, extension: string): Array<WalkEntry> { 111 4 : const files = []; 112 4 : for (const file of expandGlobSync(`${path}/**/*.${extension}`)) { 113 13 : files.push(file); 114 4 : } 115 4 : return files; 116 1 : } 117 : 118 0 : export function normaliseLink(title: string): string { 119 0 : return title.toLowerCase().replaceAll(" ", "-").replaceAll("'", "").replaceAll("(", "").replaceAll(")", "") 120 0 : } 121 : 122 1 : export function getAllMardownFiles(path: string): Array<WalkEntry> { 123 4 : return globFiles(path, 'md'); 124 1 : } 125 : 126 0 : export function getAllJupyterFiles(path: string): Array<WalkEntry> { 127 0 : return globFiles(path, 'ipynb'); 128 0 : } 129 : 130 : export interface Link { // given a link "ThisIsTheReal#Header1|This is the Given" 131 : titleName?: string // This is the Given 132 : fragment?: string // Header 1 133 : realName: string // ThisIsTheReal 134 : } 135 : 136 1 : export function parseWikiLink(wikilink: string): Link { 137 5 : const link: Link = {realName: ""} 138 5 : if (wikilink.includes("|")) { 139 7 : const parts = wikilink.split("|") 140 7 : link.titleName = parts[1] 141 7 : link.realName = parts[0] 142 7 : } else { 143 7 : link.realName = wikilink 144 5 : } 145 5 : if (link.realName.includes("#")) { 146 7 : const parts = link.realName.split("#") 147 7 : link.realName = parts[0] 148 7 : link.fragment = parts[1] 149 5 : } 150 5 : if (link.titleName == null) { 151 7 : link.titleName = link.realName 152 5 : } 153 5 : return link 154 1 : }