diff --git a/examples/musings/index.html b/examples/musings/index.html index d93719e..43c2079 100644 --- a/examples/musings/index.html +++ b/examples/musings/index.html @@ -6,10 +6,6 @@ -
- - -

     
diff --git a/modules/templates/blog.mjs b/modules/templates/blog.mjs index 75c5b87..ecac531 100644 --- a/modules/templates/blog.mjs +++ b/modules/templates/blog.mjs @@ -9,21 +9,16 @@ import { mode } from "../tickle/mode.mjs"; import { bootstrapRouter } from "../tickle/bootstrapRouter.mjs"; export const bootstrap = async () => { - const [Menu, Body, Source, Burger] = [ - "nav", + const [Body, Source] = [ "main", "#Source", - ".burger", ].map(getElementByCSSSelector); - Burger.addEventListener("click", mode.menuOpen.toggle); - - mode.loading.on(); const lines = await fetchText("files.txt"); const links = parseFileList(lines); const firstHref = links[0].href; sortFileListLines(links); - Menu.appendChild(createMenuEntriesFromFileList(links)); + Body.appendChild(createMenuEntriesFromFileList(links)); bootstrapRouter(firstHref, (content, raw) => { Body.innerHTML = ""; diff --git a/modules/tickle/bootstrapRouter.mjs b/modules/tickle/bootstrapRouter.mjs index 2ec9d72..6dbbc68 100644 --- a/modules/tickle/bootstrapRouter.mjs +++ b/modules/tickle/bootstrapRouter.mjs @@ -10,8 +10,8 @@ import { mode } from "./mode.mjs"; /** * Sets up a listener for hashchange events. * Loads the provided default Href if none is set by the user - * @param {string} defaultHref - * @param {(content: DocumentFragment, raw: string) => void} onPageLoaded + * @param {string} defaultHref + * @param {(content: DocumentFragment, raw: string) => void} onPageLoaded */ export const bootstrapRouter = async (defaultHref, onPageLoaded) => { /** @@ -27,15 +27,19 @@ export const bootstrapRouter = async (defaultHref, onPageLoaded) => { const { title, raw, content } = await fetchMarkdown(path); changeTitle(title); rewriteLocalUrls(content); - onPageLoaded(content, raw) + onPageLoaded(content, raw); mode.loading.off(); }; window.addEventListener("hashchange", onHashChange); - if (hasNoHashUrl()) { - window.location.hash = `#${defaultHref}`; - } else { - onHashChange(); - } + const load = () => { + if (hasNoHashUrl()) { + window.location.hash = `#${defaultHref}`; + } else { + onHashChange(); + } + }; + + return load }; diff --git a/modules/utils/fetchMarkdown.mjs b/modules/utils/fetchMarkdown.mjs index 0405aac..0f4275c 100644 --- a/modules/utils/fetchMarkdown.mjs +++ b/modules/utils/fetchMarkdown.mjs @@ -2,12 +2,7 @@ import { fetchText } from "./fetchText.mjs"; import { waitIfLocalHost } from "./waitIfLocalHost.mjs"; -import { generateDomFromString } from "./generateDomFromString.mjs"; -import {getFirstTitleContent} from "./getFirstTitleContent.mjs"; -// @ts-ignore -import { micromark } from "https://esm.sh/micromark@3?bundle"; -// @ts-ignore -import {frontmatter, frontmatterHtml} from 'https://esm.sh/micromark-extension-frontmatter@1?bundle' +import { markupToDom } from "./markupToDom.mjs" /** * Loads and parses a markdown document. Makes use of micromark. @@ -16,14 +11,6 @@ import {frontmatter, frontmatterHtml} from 'https://esm.sh/micromark-extension-f export const fetchMarkdown = (path) => fetchText(path) .then(waitIfLocalHost()) - .then((raw) => { - const output = micromark(raw, { - extensions: [frontmatter()], - htmlExtensions: [frontmatterHtml()] - }) - const content = generateDomFromString(output); - const title = getFirstTitleContent(content) || path.replace(/\.\w{2, 4}$/, ""); - return { title, raw, content }; - }); + .then(raw => markupToDom(raw, path)); export default fetchMarkdown; diff --git a/modules/utils/index.mjs b/modules/utils/index.mjs index d7d9346..47a8d5f 100644 --- a/modules/utils/index.mjs +++ b/modules/utils/index.mjs @@ -20,6 +20,9 @@ import { html } from "./html.mjs"; import { isExternalUrl } from "./isExternalUrl.mjs"; import { isLocalHost } from "./isLocalHost.mjs"; import { isNotNull } from "./isNotNull.mjs"; +import { markdownToMarkup } from "./markdownToMarkup.mjs"; +import { markupToDom } from "./markupToDom.mjs"; +import { memoize } from "./memoize.mjs"; import { not } from "./not.mjs"; import { onDocumentKeyUp, @@ -56,6 +59,9 @@ export { isExternalUrl, isLocalHost, isNotNull, + markdownToMarkup, + markupToDom, + memoize, not, onDocumentKeyUp, onDocumentKeyDown, diff --git a/modules/utils/markdownToMarkup.mjs b/modules/utils/markdownToMarkup.mjs new file mode 100644 index 0000000..41c8b09 --- /dev/null +++ b/modules/utils/markdownToMarkup.mjs @@ -0,0 +1,28 @@ +//@ts-check + +//@ts-ignore +import { micromark } from "https://esm.sh/micromark@3?bundle"; +//@ts-ignore +import {frontmatter, frontmatterHtml} from 'https://esm.sh/micromark-extension-frontmatter@1?bundle' +//@ts-ignore +import Yaml from 'https://esm.sh/yaml@2?bundle' + +/** + * Transforms a markup string into a valid markup. + * If there's a YAML frontmatter, will parse it too + * @param {string} markdownStr + * @returns + */ +export const markdownToMarkup = (markdownStr) => { + /** @type {string} */ + const markup = micromark(markdownStr, { + extensions: [frontmatter()], + htmlExtensions: [frontmatterHtml()] + }) + const header = Yaml.parseAllDocuments(markdownStr)[0] + /** @type {Record} */ + const frontMatter = header ? header.toJS() : {} + return { markup, frontMatter } +} + +export default markdownToMarkup \ No newline at end of file diff --git a/modules/utils/markupToDom.mjs b/modules/utils/markupToDom.mjs new file mode 100644 index 0000000..c206bcb --- /dev/null +++ b/modules/utils/markupToDom.mjs @@ -0,0 +1,20 @@ +//@ts-check +import { generateDomFromString } from "./generateDomFromString.mjs"; +import { getFirstTitleContent } from "./getFirstTitleContent.mjs"; +import { markdownToMarkup } from './markdownToMarkup.mjs'; + + +/** + * Takes a markdown string and transforms it into dom that can be slotted in a + * document. + * Additionally, it parses the frontmatter, and attempts to extract a title + * by finding either the first title in the doc, or the filename (if provided). + * @param {string} markdownStr + * @param {string} [path] the file path + */ +export const markupToDom = (markdownStr, path = '') => { + const { frontMatter, markup } = markdownToMarkup(markdownStr); + const content = generateDomFromString(markup); + const title = getFirstTitleContent(content) || path.replace(/\.\w{2, 4}$/, ""); + return { title, raw: markdownStr, content, frontMatter }; +}; diff --git a/modules/utils/memoize.mjs b/modules/utils/memoize.mjs new file mode 100644 index 0000000..2fc84ce --- /dev/null +++ b/modules/utils/memoize.mjs @@ -0,0 +1,30 @@ +//@ts-check + +/** + * Caches the result of a function. + * The cache is available as `.cache` in case there's a need to clear anything. + * @template {(...args: any[]) => any} T + * @param {T} functionToMemoize + * @returns + */ +export const memoize = (functionToMemoize) => { + /** @type {Map[0], ReturnType>} */ + const cache = new Map() + /** + * + * @param {Parameters} args + * @returns {ReturnType} + */ + const memoized = (...args) => { + const key = args[0] + if(!cache.has(key)){ + cache.set(key, functionToMemoize(...args)) + } + //@ts-ignore + return cache.get(key) + } + memoized.map = cache + return memoized +} + +export default memoize \ No newline at end of file