testing moving the functionality into small modules #5
@ -6,10 +6,6 @@
|
|||||||
<link href="../../modules/templates/blog.css" rel="stylesheet" />
|
<link href="../../modules/templates/blog.css" rel="stylesheet" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<section>
|
|
||||||
<button class="burger">≡</button>
|
|
||||||
<nav class="menu"></nav>
|
|
||||||
</section>
|
|
||||||
<main></main>
|
<main></main>
|
||||||
<pre id="Source"></pre>
|
<pre id="Source"></pre>
|
||||||
<div id="Loading">
|
<div id="Loading">
|
||||||
|
@ -9,21 +9,16 @@ import { mode } from "../tickle/mode.mjs";
|
|||||||
import { bootstrapRouter } from "../tickle/bootstrapRouter.mjs";
|
import { bootstrapRouter } from "../tickle/bootstrapRouter.mjs";
|
||||||
|
|
||||||
export const bootstrap = async () => {
|
export const bootstrap = async () => {
|
||||||
const [Menu, Body, Source, Burger] = [
|
const [Body, Source] = [
|
||||||
"nav",
|
|
||||||
"main",
|
"main",
|
||||||
"#Source",
|
"#Source",
|
||||||
".burger",
|
|
||||||
].map(getElementByCSSSelector);
|
].map(getElementByCSSSelector);
|
||||||
|
|
||||||
Burger.addEventListener("click", mode.menuOpen.toggle);
|
|
||||||
|
|
||||||
mode.loading.on();
|
|
||||||
const lines = await fetchText("files.txt");
|
const lines = await fetchText("files.txt");
|
||||||
const links = parseFileList(lines);
|
const links = parseFileList(lines);
|
||||||
const firstHref = links[0].href;
|
const firstHref = links[0].href;
|
||||||
sortFileListLines(links);
|
sortFileListLines(links);
|
||||||
Menu.appendChild(createMenuEntriesFromFileList(links));
|
Body.appendChild(createMenuEntriesFromFileList(links));
|
||||||
|
|
||||||
bootstrapRouter(firstHref, (content, raw) => {
|
bootstrapRouter(firstHref, (content, raw) => {
|
||||||
Body.innerHTML = "";
|
Body.innerHTML = "";
|
||||||
|
@ -10,8 +10,8 @@ import { mode } from "./mode.mjs";
|
|||||||
/**
|
/**
|
||||||
* Sets up a listener for hashchange events.
|
* Sets up a listener for hashchange events.
|
||||||
* Loads the provided default Href if none is set by the user
|
* Loads the provided default Href if none is set by the user
|
||||||
* @param {string} defaultHref
|
* @param {string} defaultHref
|
||||||
* @param {(content: DocumentFragment, raw: string) => void} onPageLoaded
|
* @param {(content: DocumentFragment, raw: string) => void} onPageLoaded
|
||||||
*/
|
*/
|
||||||
export const bootstrapRouter = async (defaultHref, onPageLoaded) => {
|
export const bootstrapRouter = async (defaultHref, onPageLoaded) => {
|
||||||
/**
|
/**
|
||||||
@ -27,15 +27,19 @@ export const bootstrapRouter = async (defaultHref, onPageLoaded) => {
|
|||||||
const { title, raw, content } = await fetchMarkdown(path);
|
const { title, raw, content } = await fetchMarkdown(path);
|
||||||
changeTitle(title);
|
changeTitle(title);
|
||||||
rewriteLocalUrls(content);
|
rewriteLocalUrls(content);
|
||||||
onPageLoaded(content, raw)
|
onPageLoaded(content, raw);
|
||||||
mode.loading.off();
|
mode.loading.off();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("hashchange", onHashChange);
|
window.addEventListener("hashchange", onHashChange);
|
||||||
|
|
||||||
if (hasNoHashUrl()) {
|
const load = () => {
|
||||||
window.location.hash = `#${defaultHref}`;
|
if (hasNoHashUrl()) {
|
||||||
} else {
|
window.location.hash = `#${defaultHref}`;
|
||||||
onHashChange();
|
} else {
|
||||||
}
|
onHashChange();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return load
|
||||||
};
|
};
|
||||||
|
@ -2,12 +2,7 @@
|
|||||||
|
|
||||||
import { fetchText } from "./fetchText.mjs";
|
import { fetchText } from "./fetchText.mjs";
|
||||||
import { waitIfLocalHost } from "./waitIfLocalHost.mjs";
|
import { waitIfLocalHost } from "./waitIfLocalHost.mjs";
|
||||||
import { generateDomFromString } from "./generateDomFromString.mjs";
|
import { markupToDom } from "./markupToDom.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'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads and parses a markdown document. Makes use of micromark.
|
* 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) =>
|
export const fetchMarkdown = (path) =>
|
||||||
fetchText(path)
|
fetchText(path)
|
||||||
.then(waitIfLocalHost())
|
.then(waitIfLocalHost())
|
||||||
.then((raw) => {
|
.then(raw => markupToDom(raw, path));
|
||||||
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 };
|
|
||||||
});
|
|
||||||
|
|
||||||
export default fetchMarkdown;
|
export default fetchMarkdown;
|
||||||
|
@ -20,6 +20,9 @@ import { html } from "./html.mjs";
|
|||||||
import { isExternalUrl } from "./isExternalUrl.mjs";
|
import { isExternalUrl } from "./isExternalUrl.mjs";
|
||||||
import { isLocalHost } from "./isLocalHost.mjs";
|
import { isLocalHost } from "./isLocalHost.mjs";
|
||||||
import { isNotNull } from "./isNotNull.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 { not } from "./not.mjs";
|
||||||
import {
|
import {
|
||||||
onDocumentKeyUp,
|
onDocumentKeyUp,
|
||||||
@ -56,6 +59,9 @@ export {
|
|||||||
isExternalUrl,
|
isExternalUrl,
|
||||||
isLocalHost,
|
isLocalHost,
|
||||||
isNotNull,
|
isNotNull,
|
||||||
|
markdownToMarkup,
|
||||||
|
markupToDom,
|
||||||
|
memoize,
|
||||||
not,
|
not,
|
||||||
onDocumentKeyUp,
|
onDocumentKeyUp,
|
||||||
onDocumentKeyDown,
|
onDocumentKeyDown,
|
||||||
|
28
modules/utils/markdownToMarkup.mjs
Normal file
28
modules/utils/markdownToMarkup.mjs
Normal file
@ -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<string, unknown>} */
|
||||||
|
const frontMatter = header ? header.toJS() : {}
|
||||||
|
return { markup, frontMatter }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default markdownToMarkup
|
20
modules/utils/markupToDom.mjs
Normal file
20
modules/utils/markupToDom.mjs
Normal file
@ -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 };
|
||||||
|
};
|
30
modules/utils/memoize.mjs
Normal file
30
modules/utils/memoize.mjs
Normal file
@ -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<Parameters<T>[0], ReturnType<T>>} */
|
||||||
|
const cache = new Map()
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Parameters<T>} args
|
||||||
|
* @returns {ReturnType<T>}
|
||||||
|
*/
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user