mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
init
This commit is contained in:
56
bin/utils/markdown/index.ts
Normal file
56
bin/utils/markdown/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { resolve } from "path";
|
||||
import { decode } from "html-entities";
|
||||
import { marked } from "marked";
|
||||
import remark from "remark";
|
||||
import visit from "unist-util-visit";
|
||||
import { Bundler } from "../../bundler";
|
||||
import { createImage } from "../../resources/image";
|
||||
import { renderer } from "./latex";
|
||||
|
||||
type MarkdownBundleImagesOptions = {
|
||||
cwd: string;
|
||||
content: string;
|
||||
bundler: Bundler;
|
||||
};
|
||||
|
||||
const markdownBundleImages = async ({
|
||||
bundler,
|
||||
cwd,
|
||||
content,
|
||||
}: MarkdownBundleImagesOptions) => {
|
||||
const result = await remark()
|
||||
.use(() => (tree) => {
|
||||
visit(tree, "image", (node) => {
|
||||
if (!("url" in node)) {
|
||||
return;
|
||||
}
|
||||
const url = node.url as string;
|
||||
const path = resolve(cwd, url);
|
||||
const image = createImage({
|
||||
image: path,
|
||||
bundler,
|
||||
format: "webp",
|
||||
});
|
||||
const newUrl = image;
|
||||
node.url = newUrl;
|
||||
});
|
||||
})
|
||||
.process(content);
|
||||
return String(result);
|
||||
};
|
||||
|
||||
type MarkdownToLatexOptions = {
|
||||
root: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
const markdownToLatex = ({ root, content }: MarkdownToLatexOptions) => {
|
||||
const render: any = {
|
||||
...renderer(0),
|
||||
};
|
||||
const latex = marked(content, {
|
||||
renderer: render,
|
||||
});
|
||||
return decode(latex);
|
||||
};
|
||||
export { markdownBundleImages, markdownToLatex };
|
||||
91
bin/utils/markdown/latex.ts
Normal file
91
bin/utils/markdown/latex.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { decode } from "html-entities";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
const latexTypes = ["", "section", "subsection", "paragraph", "subparagraph"];
|
||||
|
||||
const sanitize = (text?: string) => {
|
||||
if (!text) {
|
||||
return "";
|
||||
}
|
||||
return decode(text)
|
||||
.replace("&", "\\&")
|
||||
.replace("_", "\\_")
|
||||
.replace(/([^\\])\}/g, "$1\\}")
|
||||
.replace(/([^\\])\{/g, "$1\\{")
|
||||
.replace(/[^\\]\[/g, "\\[")
|
||||
.replace(/#/g, "\\#");
|
||||
};
|
||||
|
||||
type Renderer = (depth: number) => {
|
||||
heading?: (text: string, depth: number) => string;
|
||||
code?: (input: string) => string;
|
||||
text?: (input: string) => string;
|
||||
paragraph?: (input: string) => string;
|
||||
list?: (input: string) => string;
|
||||
listitem?: (input: string) => string;
|
||||
link?: (href: string, text: string) => string;
|
||||
strong?: (text: string) => string;
|
||||
em?: (text: string) => string;
|
||||
codespan?: (code: string) => string;
|
||||
image?: (link: string) => string;
|
||||
};
|
||||
|
||||
const renderer = (outerDepth: number) => ({
|
||||
heading: (text: string, depth: number) => {
|
||||
return `\\${latexTypes[outerDepth + depth]}{${sanitize(text)}}\n\n`;
|
||||
},
|
||||
code: (input: string) => {
|
||||
return `
|
||||
\\begin{lstlisting}
|
||||
${input}
|
||||
\\end{lstlisting}
|
||||
`;
|
||||
},
|
||||
text: (input: string) => {
|
||||
return sanitize(input);
|
||||
},
|
||||
blockquote: (input: string) => {
|
||||
return sanitize(input);
|
||||
},
|
||||
paragraph: (input: string) => {
|
||||
return `${input}\n\n`;
|
||||
},
|
||||
list: (input: string) => {
|
||||
return `
|
||||
\\begin{itemize}
|
||||
${input}
|
||||
\\end{itemize}
|
||||
`;
|
||||
},
|
||||
listitem: (input: string) => {
|
||||
return `\\item{${input}}`;
|
||||
},
|
||||
link: (href: string, text: string) => {
|
||||
if (!text || text === href) {
|
||||
return `\\url{${sanitize(href)}}`;
|
||||
}
|
||||
return `${sanitize(text)} (\\url{${sanitize(href)}})`;
|
||||
},
|
||||
strong: (text: string) => {
|
||||
return `\\textbf{${sanitize(text)}}`;
|
||||
},
|
||||
em: (text: string) => {
|
||||
return `\\textbf{${sanitize(text)}}`;
|
||||
},
|
||||
codespan: (code: string) => {
|
||||
return `\\texttt{${sanitize(code)}}`;
|
||||
},
|
||||
image: (link: string) => {
|
||||
if (!existsSync(link)) {
|
||||
return "Online image not supported";
|
||||
}
|
||||
return `\\begin{figure}[h!]
|
||||
\\includegraphics[width=0.5\\textwidth]{${link}}
|
||||
\\centering
|
||||
\\end{figure}
|
||||
`;
|
||||
},
|
||||
});
|
||||
|
||||
export type { Renderer };
|
||||
export { sanitize, renderer };
|
||||
27
bin/utils/observable/index.ts
Normal file
27
bin/utils/observable/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Observable } from "../../observable";
|
||||
|
||||
const forEach = async <T extends Observable<any[]>>(
|
||||
observable: T,
|
||||
fn: (
|
||||
value: T extends Observable<infer U>
|
||||
? U extends Array<infer A>
|
||||
? A
|
||||
: never
|
||||
: never
|
||||
) => Promise<void>
|
||||
) => {
|
||||
const knownValues = new Set();
|
||||
const update = async () => {
|
||||
for (let value of await observable.data) {
|
||||
if (knownValues.has(value)) {
|
||||
continue;
|
||||
}
|
||||
await fn(value);
|
||||
knownValues.add(value);
|
||||
}
|
||||
};
|
||||
await update();
|
||||
observable.subscribe(update);
|
||||
};
|
||||
|
||||
export { forEach };
|
||||
Reference in New Issue
Block a user