diff --git a/.gitignore b/.gitignore index 657842b..c75c89e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ node_modules/ .pnp.js # Build output -dist/ +# dist/ is committed so that GitHub-based installs work without build tools build/ *.tsbuildinfo diff --git a/dist/dom.d.ts b/dist/dom.d.ts new file mode 100644 index 0000000..434236c --- /dev/null +++ b/dist/dom.d.ts @@ -0,0 +1,5 @@ +declare function removeAllChildren(el: HTMLElement): void; +declare function registerEscapeHandler(outsideContainer: HTMLElement | null, onEscape: () => void): () => void; +declare function normalizeRelativeURLs(html: Document, baseUrl: string): void; + +export { normalizeRelativeURLs, registerEscapeHandler, removeAllChildren }; diff --git a/dist/dom.js b/dist/dom.js new file mode 100644 index 0000000..e4fae17 --- /dev/null +++ b/dist/dom.js @@ -0,0 +1,56 @@ +// src/dom.ts +function removeAllChildren(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} +function registerEscapeHandler(outsideContainer, onEscape) { + if (!outsideContainer) return () => { + }; + const onClick = (e) => { + if (!outsideContainer.classList.contains("active")) { + return; + } + if (e.target === outsideContainer) { + e.preventDefault(); + e.stopPropagation(); + onEscape(); + } + }; + const onKeydown = (e) => { + if (!outsideContainer.classList.contains("active")) { + return; + } + if (e.key === "Escape") { + e.preventDefault(); + onEscape(); + } + }; + outsideContainer.addEventListener("click", onClick); + document.addEventListener("keydown", onKeydown); + return () => { + outsideContainer.removeEventListener("click", onClick); + document.removeEventListener("keydown", onKeydown); + }; +} +function normalizeRelativeURLs(html, baseUrl) { + const elements = html.querySelectorAll("[src], [href]"); + for (const el of Array.from(elements)) { + const attr = el.hasAttribute("href") ? "href" : "src"; + const val = el.getAttribute(attr); + if (!val) continue; + if (val.startsWith("http://") || val.startsWith("https://") || val.startsWith("mailto:") || val.startsWith("tel:") || val.startsWith("#") || val.startsWith("/") || val.startsWith("data:")) { + continue; + } + try { + const normalized = new URL(val, baseUrl).toString(); + el.setAttribute(attr, normalized); + } catch { + continue; + } + } +} + +export { normalizeRelativeURLs, registerEscapeHandler, removeAllChildren }; +//# sourceMappingURL=dom.js.map +//# sourceMappingURL=dom.js.map \ No newline at end of file diff --git a/dist/dom.js.map b/dist/dom.js.map new file mode 100644 index 0000000..7e5e5f1 --- /dev/null +++ b/dist/dom.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/dom.ts"],"names":[],"mappings":";AAAO,SAAS,kBAAkB,EAAA,EAAuB;AACvD,EAAA,OAAO,GAAG,UAAA,EAAY;AACpB,IAAA,EAAA,CAAG,WAAA,CAAY,GAAG,UAAU,CAAA;AAAA,EAC9B;AACF;AAEO,SAAS,qBAAA,CACd,kBACA,QAAA,EACY;AACZ,EAAA,IAAI,CAAC,gBAAA,EAAkB,OAAO,MAAM;AAAA,EAAC,CAAA;AAErC,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAkB;AACjC,IAAA,IAAI,CAAC,gBAAA,CAAiB,SAAA,CAAU,QAAA,CAAS,QAAQ,CAAA,EAAG;AAClD,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,WAAW,gBAAA,EAAkB;AACjC,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,MAAA,QAAA,EAAS;AAAA,IACX;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAqB;AACtC,IAAA,IAAI,CAAC,gBAAA,CAAiB,SAAA,CAAU,QAAA,CAAS,QAAQ,CAAA,EAAG;AAClD,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,QAAA,EAAS;AAAA,IACX;AAAA,EACF,CAAA;AAEA,EAAA,gBAAA,CAAiB,gBAAA,CAAiB,SAAS,OAAO,CAAA;AAClD,EAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAE9C,EAAA,OAAO,MAAM;AACX,IAAA,gBAAA,CAAiB,mBAAA,CAAoB,SAAS,OAAO,CAAA;AACrD,IAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAAA,EACnD,CAAA;AACF;AAEO,SAAS,qBAAA,CAAsB,MAAgB,OAAA,EAAuB;AAC3E,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,gBAAA,CAA8B,eAAe,CAAA;AACnE,EAAA,KAAA,MAAW,EAAA,IAAM,KAAA,CAAM,IAAA,CAAK,QAAQ,CAAA,EAAG;AACrC,IAAA,MAAM,IAAA,GAAO,EAAA,CAAG,YAAA,CAAa,MAAM,IAAI,MAAA,GAAS,KAAA;AAChD,IAAA,MAAM,GAAA,GAAM,EAAA,CAAG,YAAA,CAAa,IAAI,CAAA;AAChC,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,IACE,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,IACxB,GAAA,CAAI,UAAA,CAAW,UAAU,CAAA,IACzB,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,IACxB,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,IACrB,GAAA,CAAI,UAAA,CAAW,GAAG,CAAA,IAClB,GAAA,CAAI,UAAA,CAAW,GAAG,CAAA,IAClB,GAAA,CAAI,UAAA,CAAW,OAAO,CAAA,EACtB;AACA,MAAA;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,MAAM,aAAa,IAAI,GAAA,CAAI,GAAA,EAAK,OAAO,EAAE,QAAA,EAAS;AAClD,MAAA,EAAA,CAAG,YAAA,CAAa,MAAM,UAAU,CAAA;AAAA,IAClC,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AAAA,EACF;AACF","file":"dom.js","sourcesContent":["export function removeAllChildren(el: HTMLElement): void {\n while (el.firstChild) {\n el.removeChild(el.firstChild);\n }\n}\n\nexport function registerEscapeHandler(\n outsideContainer: HTMLElement | null,\n onEscape: () => void,\n): () => void {\n if (!outsideContainer) return () => {};\n\n const onClick = (e: MouseEvent) => {\n if (!outsideContainer.classList.contains(\"active\")) {\n return;\n }\n if (e.target === outsideContainer) {\n e.preventDefault();\n e.stopPropagation();\n onEscape();\n }\n };\n\n const onKeydown = (e: KeyboardEvent) => {\n if (!outsideContainer.classList.contains(\"active\")) {\n return;\n }\n if (e.key === \"Escape\") {\n e.preventDefault();\n onEscape();\n }\n };\n\n outsideContainer.addEventListener(\"click\", onClick);\n document.addEventListener(\"keydown\", onKeydown);\n\n return () => {\n outsideContainer.removeEventListener(\"click\", onClick);\n document.removeEventListener(\"keydown\", onKeydown);\n };\n}\n\nexport function normalizeRelativeURLs(html: Document, baseUrl: string): void {\n const elements = html.querySelectorAll(\"[src], [href]\");\n for (const el of Array.from(elements)) {\n const attr = el.hasAttribute(\"href\") ? \"href\" : \"src\";\n const val = el.getAttribute(attr);\n if (!val) continue;\n if (\n val.startsWith(\"http://\") ||\n val.startsWith(\"https://\") ||\n val.startsWith(\"mailto:\") ||\n val.startsWith(\"tel:\") ||\n val.startsWith(\"#\") ||\n val.startsWith(\"/\") ||\n val.startsWith(\"data:\")\n ) {\n continue;\n }\n try {\n const normalized = new URL(val, baseUrl).toString();\n el.setAttribute(attr, normalized);\n } catch {\n continue;\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/escape.d.ts b/dist/escape.d.ts new file mode 100644 index 0000000..9be4286 --- /dev/null +++ b/dist/escape.d.ts @@ -0,0 +1,23 @@ +/** + * HTML escape utilities for safe string rendering. + */ +/** + * Escapes HTML special characters in a string to prevent XSS. + * @param unsafe - The string to escape + * @returns The escaped string safe for HTML rendering + * + * @example + * escapeHTML('') // '<script>alert("xss")</script>' + */ +declare function escapeHTML(unsafe: string): string; +/** + * Unescapes HTML entities back to their original characters. + * @param html - The HTML-escaped string + * @returns The unescaped string + * + * @example + * unescapeHTML('<div>') // '
' + */ +declare function unescapeHTML(html: string): string; + +export { escapeHTML, unescapeHTML }; diff --git a/dist/escape.js b/dist/escape.js new file mode 100644 index 0000000..5740e9a --- /dev/null +++ b/dist/escape.js @@ -0,0 +1,11 @@ +// src/escape.ts +function escapeHTML(unsafe) { + return unsafe.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); +} +function unescapeHTML(html) { + return html.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll(""", '"').replaceAll("'", "'"); +} + +export { escapeHTML, unescapeHTML }; +//# sourceMappingURL=escape.js.map +//# sourceMappingURL=escape.js.map \ No newline at end of file diff --git a/dist/escape.js.map b/dist/escape.js.map new file mode 100644 index 0000000..4aa4ee7 --- /dev/null +++ b/dist/escape.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/escape.ts"],"names":[],"mappings":";AAYO,SAAS,WAAW,MAAA,EAAwB;AACjD,EAAA,OAAO,OACJ,UAAA,CAAW,GAAA,EAAK,OAAO,CAAA,CACvB,UAAA,CAAW,KAAK,MAAM,CAAA,CACtB,WAAW,GAAA,EAAK,MAAM,EACtB,UAAA,CAAW,GAAA,EAAK,QAAQ,CAAA,CACxB,UAAA,CAAW,KAAK,QAAQ,CAAA;AAC7B;AAUO,SAAS,aAAa,IAAA,EAAsB;AACjD,EAAA,OAAO,KACJ,UAAA,CAAW,OAAA,EAAS,GAAG,CAAA,CACvB,UAAA,CAAW,QAAQ,GAAG,CAAA,CACtB,WAAW,MAAA,EAAQ,GAAG,EACtB,UAAA,CAAW,QAAA,EAAU,GAAG,CAAA,CACxB,UAAA,CAAW,UAAU,GAAG,CAAA;AAC7B","file":"escape.js","sourcesContent":["/**\n * HTML escape utilities for safe string rendering.\n */\n\n/**\n * Escapes HTML special characters in a string to prevent XSS.\n * @param unsafe - The string to escape\n * @returns The escaped string safe for HTML rendering\n *\n * @example\n * escapeHTML('') // '<script>alert("xss")</script>'\n */\nexport function escapeHTML(unsafe: string): string {\n return unsafe\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\n/**\n * Unescapes HTML entities back to their original characters.\n * @param html - The HTML-escaped string\n * @returns The unescaped string\n *\n * @example\n * unescapeHTML('<div>') // '
'\n */\nexport function unescapeHTML(html: string): string {\n return html\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\""\", '\"')\n .replaceAll(\"'\", \"'\");\n}\n"]} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..4ec3dec --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,8 @@ +export { RelativeURL, SimpleSlug, TransformOptions, endsWith, getAllSegmentPrefixes, getFileExtension, getFullSlug, getFullSlugFromUrl, isAbsoluteURL, isFilePath, isFolderPath, isFullSlug, isRelativeURL, isSimpleSlug, joinSegments, pathToRoot, resolvePath, resolveRelative, simplifySlug, slugTag, slugifyFilePath, splitAnchor, stripSlashes, transformInternalLink, transformLink, trimSuffix } from './path.js'; +export { normalizeRelativeURLs, registerEscapeHandler, removeAllChildren } from './dom.js'; +export { capitalize, classNames } from './lang.js'; +export { escapeHTML, unescapeHTML } from './escape.js'; +export { htmlToJsx } from './jsx.js'; +export { FilePath, FullSlug } from '@quartz-community/types'; +import 'hast-util-to-jsx-runtime'; +import 'hast'; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..875a5db --- /dev/null +++ b/dist/index.js @@ -0,0 +1,280 @@ +import { slug } from 'github-slugger'; +import { toJsxRuntime } from 'hast-util-to-jsx-runtime'; +import { jsxs, jsx, Fragment } from 'preact/jsx-runtime'; +import { h } from 'preact'; + +// src/path.ts +function isFilePath(s) { + const validStart = !s.startsWith("."); + return validStart && _hasFileExtension(s); +} +function isFullSlug(s) { + const validStart = !(s.startsWith(".") || s.startsWith("/")); + const validEnding = !s.endsWith("/"); + return validStart && validEnding && !_containsForbiddenCharacters(s); +} +function isSimpleSlug(s) { + const validStart = !(s.startsWith(".") || s.length > 1 && s.startsWith("/")); + const validEnding = !endsWith(s, "index"); + return validStart && !_containsForbiddenCharacters(s) && validEnding && !_hasFileExtension(s); +} +function isRelativeURL(s) { + const validStart = /^\.{1,2}/.test(s); + const validEnding = !endsWith(s, "index"); + return validStart && validEnding && ![".md", ".html"].includes(getFileExtension(s) ?? ""); +} +function isAbsoluteURL(s) { + try { + new URL(s); + } catch { + return false; + } + return true; +} +function simplifySlug(fp) { + const res = stripSlashes(trimSuffix(fp, "index"), true); + return res.length === 0 ? "/" : res; +} +function getFullSlug(window2) { + const res = window2.document.body.dataset.slug; + return res; +} +function getFullSlugFromUrl() { + let rawSlug = window.location.pathname; + if (rawSlug.endsWith("/")) rawSlug = rawSlug.slice(0, -1); + if (rawSlug.startsWith("/")) rawSlug = rawSlug.slice(1); + return rawSlug; +} +function slugifyFilePath(fp, excludeExt) { + fp = stripSlashes(fp); + const ext = getFileExtension(fp); + const withoutFileExt = fp.replace(new RegExp(ext + "$"), ""); + const finalExt = excludeExt || [".md", ".html", void 0].includes(ext) ? "" : ext; + let slug = _sluggify(withoutFileExt); + if (endsWith(slug, "_index")) { + slug = slug.replace(/_index$/, "index"); + } + return slug + (finalExt ?? ""); +} +function joinSegments(...args) { + if (args.length === 0) { + return ""; + } + let joined = args.filter((segment) => segment !== "" && segment !== "/").map((segment) => stripSlashes(segment)).join("/"); + const first = args[0]; + const last = args[args.length - 1]; + if (first?.startsWith("/")) { + joined = "/" + joined; + } + if (last?.endsWith("/")) { + joined = joined + "/"; + } + return joined; +} +function resolvePath(to) { + if (to.startsWith("/")) return to; + return "/" + to; +} +function endsWith(s, suffix) { + return s === suffix || s.endsWith("/" + suffix); +} +function trimSuffix(s, suffix) { + if (endsWith(s, suffix)) { + s = s.slice(0, -suffix.length); + } + return s; +} +function stripSlashes(s, onlyStripPrefix) { + if (s.startsWith("/")) { + s = s.substring(1); + } + if (!onlyStripPrefix && s.endsWith("/")) { + s = s.slice(0, -1); + } + return s; +} +function getFileExtension(s) { + return s.match(/\.[A-Za-z0-9]+$/)?.[0]; +} +function isFolderPath(fplike) { + return fplike.endsWith("/") || endsWith(fplike, "index") || endsWith(fplike, "index.md") || endsWith(fplike, "index.html"); +} +function getAllSegmentPrefixes(path) { + const segments = path.split("/"); + const results = []; + for (let i = 0; i < segments.length; i++) { + results.push(segments.slice(0, i + 1).join("/")); + } + return results; +} +function pathToRoot(slug) { + let rootPath = slug.split("/").filter((x) => x !== "").slice(0, -1).map((_) => "..").join("/"); + if (rootPath.length === 0) { + rootPath = "."; + } + return rootPath; +} +function resolveRelative(current, target) { + const res = joinSegments(pathToRoot(current), simplifySlug(target)); + return res; +} +function splitAnchor(link) { + const [fp, anchor] = link.split("#", 2); + if (fp.endsWith(".pdf")) { + return [fp, anchor === void 0 ? "" : `#${anchor}`]; + } + const slugged = anchor === void 0 ? "" : "#" + slug(anchor); + return [fp, slugged]; +} +function slugTag(tag) { + return tag.split("/").map((tagSegment) => _sluggify(tagSegment)).join("/"); +} +function transformInternalLink(link) { + const [fplike, anchor] = splitAnchor(decodeURI(link)); + const folderPath = isFolderPath(fplike); + const segments = fplike.split("/").filter((x) => x.length > 0); + const prefix = segments.filter(_isRelativeSegment).join("/"); + const fp = segments.filter((seg) => !_isRelativeSegment(seg) && seg !== "").join("/"); + const simpleSlug = simplifySlug(slugifyFilePath(fp)); + const joined = joinSegments(stripSlashes(prefix), stripSlashes(simpleSlug)); + const trail = folderPath ? "/" : ""; + const res = _addRelativeToStart(joined) + trail + anchor; + return res; +} +function transformLink(src, target, opts) { + const targetSlug = transformInternalLink(target); + if (opts.strategy === "relative") { + return targetSlug; + } else { + const folderTail = isFolderPath(targetSlug) ? "/" : ""; + const canonicalSlug = stripSlashes(targetSlug.slice(".".length)); + const [targetCanonical, targetAnchor] = splitAnchor(canonicalSlug); + if (opts.strategy === "shortest") { + const matchingFileNames = opts.allSlugs.filter((slug) => { + const parts = slug.split("/"); + const fileName = parts.at(-1); + return targetCanonical === fileName; + }); + if (matchingFileNames.length === 1) { + const matchedSlug = matchingFileNames[0]; + return resolveRelative(src, matchedSlug) + targetAnchor; + } + } + return joinSegments(pathToRoot(src), canonicalSlug) + folderTail; + } +} +function _sluggify(s) { + return s.split("/").map( + (segment) => segment.replace(/\s/g, "-").replace(/&/g, "-and-").replace(/%/g, "-percent").replace(/\?/g, "").replace(/#/g, "") + ).join("/").replace(/\/$/, ""); +} +function _containsForbiddenCharacters(s) { + return s.includes(" ") || s.includes("#") || s.includes("?") || s.includes("&"); +} +function _hasFileExtension(s) { + return getFileExtension(s) !== void 0; +} +function _isRelativeSegment(s) { + return /^\.{0,2}$/.test(s); +} +function _addRelativeToStart(s) { + if (s === "") { + s = "."; + } + if (!s.startsWith(".")) { + s = joinSegments(".", s); + } + return s; +} + +// src/dom.ts +function removeAllChildren(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} +function registerEscapeHandler(outsideContainer, onEscape) { + if (!outsideContainer) return () => { + }; + const onClick = (e) => { + if (!outsideContainer.classList.contains("active")) { + return; + } + if (e.target === outsideContainer) { + e.preventDefault(); + e.stopPropagation(); + onEscape(); + } + }; + const onKeydown = (e) => { + if (!outsideContainer.classList.contains("active")) { + return; + } + if (e.key === "Escape") { + e.preventDefault(); + onEscape(); + } + }; + outsideContainer.addEventListener("click", onClick); + document.addEventListener("keydown", onKeydown); + return () => { + outsideContainer.removeEventListener("click", onClick); + document.removeEventListener("keydown", onKeydown); + }; +} +function normalizeRelativeURLs(html, baseUrl) { + const elements = html.querySelectorAll("[src], [href]"); + for (const el of Array.from(elements)) { + const attr = el.hasAttribute("href") ? "href" : "src"; + const val = el.getAttribute(attr); + if (!val) continue; + if (val.startsWith("http://") || val.startsWith("https://") || val.startsWith("mailto:") || val.startsWith("tel:") || val.startsWith("#") || val.startsWith("/") || val.startsWith("data:")) { + continue; + } + try { + const normalized = new URL(val, baseUrl).toString(); + el.setAttribute(attr, normalized); + } catch { + continue; + } + } +} + +// src/lang.ts +function capitalize(s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); +} +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +// src/escape.ts +function escapeHTML(unsafe) { + return unsafe.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); +} +function unescapeHTML(html) { + return html.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll(""", '"').replaceAll("'", "'"); +} +function childrenToString(children) { + if (typeof children === "string") return children; + if (Array.isArray(children)) return children.map(childrenToString).join(""); + return String(children ?? ""); +} +var builtinComponents = { + table: (props) => /* @__PURE__ */ jsx("div", { class: "table-container", children: /* @__PURE__ */ jsx("table", { ...props }) }), + style: ({ children, ...rest }) => h("style", { ...rest, dangerouslySetInnerHTML: { __html: childrenToString(children) } }), + script: ({ children, ...rest }) => h("script", { ...rest, dangerouslySetInnerHTML: { __html: childrenToString(children) } }) +}; +function htmlToJsx(tree, components) { + return toJsxRuntime(tree, { + Fragment, + jsx, + jsxs, + elementAttributeNameCase: "html", + components: { ...builtinComponents, ...components } + }); +} + +export { capitalize, classNames, endsWith, escapeHTML, getAllSegmentPrefixes, getFileExtension, getFullSlug, getFullSlugFromUrl, htmlToJsx, isAbsoluteURL, isFilePath, isFolderPath, isFullSlug, isRelativeURL, isSimpleSlug, joinSegments, normalizeRelativeURLs, pathToRoot, registerEscapeHandler, removeAllChildren, resolvePath, resolveRelative, simplifySlug, slugTag, slugifyFilePath, splitAnchor, stripSlashes, transformInternalLink, transformLink, trimSuffix, unescapeHTML }; +//# sourceMappingURL=index.js.map +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..78d7cbd --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/path.ts","../src/dom.ts","../src/lang.ts","../src/escape.ts","../src/jsx.tsx"],"names":["window","slugAnchor","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import { slug as slugAnchor } from \"github-slugger\";\nimport type { FilePath, FullSlug } from \"@quartz-community/types\";\n\nexport type { FilePath, FullSlug };\n\n/** No '/index' ending, no file extension, can have trailing slash for folders. */\nexport type SimpleSlug = string & { _brand: \"SimpleSlug\" };\n\n/** Starts with './' or '../', used for navigation. */\nexport type RelativeURL = string & { _brand: \"RelativeURL\" };\n\nexport interface TransformOptions {\n strategy: \"absolute\" | \"relative\" | \"shortest\";\n allSlugs: FullSlug[];\n}\n\nexport function isFilePath(s: string): s is FilePath {\n const validStart = !s.startsWith(\".\");\n return validStart && _hasFileExtension(s);\n}\n\nexport function isFullSlug(s: string): s is FullSlug {\n const validStart = !(s.startsWith(\".\") || s.startsWith(\"/\"));\n const validEnding = !s.endsWith(\"/\");\n return validStart && validEnding && !_containsForbiddenCharacters(s);\n}\n\nexport function isSimpleSlug(s: string): s is SimpleSlug {\n const validStart = !(s.startsWith(\".\") || (s.length > 1 && s.startsWith(\"/\")));\n const validEnding = !endsWith(s, \"index\");\n return validStart && !_containsForbiddenCharacters(s) && validEnding && !_hasFileExtension(s);\n}\n\nexport function isRelativeURL(s: string): s is RelativeURL {\n const validStart = /^\\.{1,2}/.test(s);\n const validEnding = !endsWith(s, \"index\");\n return validStart && validEnding && ![\".md\", \".html\"].includes(getFileExtension(s) ?? \"\");\n}\n\nexport function isAbsoluteURL(s: string): boolean {\n try {\n new URL(s);\n } catch {\n return false;\n }\n return true;\n}\n\nexport function simplifySlug(fp: FullSlug | string): SimpleSlug {\n const res = stripSlashes(trimSuffix(fp, \"index\"), true);\n return (res.length === 0 ? \"/\" : res) as SimpleSlug;\n}\n\nexport function getFullSlug(window: Window): FullSlug {\n const res = window.document.body.dataset.slug! as FullSlug;\n return res;\n}\n\nexport function getFullSlugFromUrl(): FullSlug {\n let rawSlug = window.location.pathname;\n if (rawSlug.endsWith(\"/\")) rawSlug = rawSlug.slice(0, -1);\n if (rawSlug.startsWith(\"/\")) rawSlug = rawSlug.slice(1);\n return rawSlug as FullSlug;\n}\n\nexport function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug {\n fp = stripSlashes(fp) as FilePath;\n const ext = getFileExtension(fp);\n const withoutFileExt = fp.replace(new RegExp(ext + \"$\"), \"\");\n const finalExt = excludeExt || [\".md\", \".html\", undefined].includes(ext) ? \"\" : ext;\n\n let slug = _sluggify(withoutFileExt);\n\n if (endsWith(slug, \"_index\")) {\n slug = slug.replace(/_index$/, \"index\");\n }\n\n return (slug + (finalExt ?? \"\")) as FullSlug;\n}\n\nexport function joinSegments(...args: string[]): string {\n if (args.length === 0) {\n return \"\";\n }\n\n let joined = args\n .filter((segment) => segment !== \"\" && segment !== \"/\")\n .map((segment) => stripSlashes(segment))\n .join(\"/\");\n\n const first = args[0];\n const last = args[args.length - 1];\n\n if (first?.startsWith(\"/\")) {\n joined = \"/\" + joined;\n }\n\n if (last?.endsWith(\"/\")) {\n joined = joined + \"/\";\n }\n\n return joined;\n}\n\nexport function resolvePath(to: string): string {\n if (to.startsWith(\"/\")) return to;\n return \"/\" + to;\n}\n\nexport function endsWith(s: string, suffix: string): boolean {\n return s === suffix || s.endsWith(\"/\" + suffix);\n}\n\nexport function trimSuffix(s: string, suffix: string): string {\n if (endsWith(s, suffix)) {\n s = s.slice(0, -suffix.length);\n }\n return s;\n}\n\nexport function stripSlashes(s: string, onlyStripPrefix?: boolean): string {\n if (s.startsWith(\"/\")) {\n s = s.substring(1);\n }\n\n if (!onlyStripPrefix && s.endsWith(\"/\")) {\n s = s.slice(0, -1);\n }\n\n return s;\n}\n\nexport function getFileExtension(s: string): string | undefined {\n return s.match(/\\.[A-Za-z0-9]+$/)?.[0];\n}\n\nexport function isFolderPath(fplike: string): boolean {\n return (\n fplike.endsWith(\"/\") ||\n endsWith(fplike, \"index\") ||\n endsWith(fplike, \"index.md\") ||\n endsWith(fplike, \"index.html\")\n );\n}\n\nexport function getAllSegmentPrefixes(path: string): string[] {\n const segments = path.split(\"/\");\n const results: string[] = [];\n for (let i = 0; i < segments.length; i++) {\n results.push(segments.slice(0, i + 1).join(\"/\"));\n }\n return results;\n}\n\nexport function pathToRoot(slug: FullSlug): RelativeURL {\n let rootPath = slug\n .split(\"/\")\n .filter((x) => x !== \"\")\n .slice(0, -1)\n .map((_) => \"..\")\n .join(\"/\");\n\n if (rootPath.length === 0) {\n rootPath = \".\";\n }\n\n return rootPath as RelativeURL;\n}\n\nexport function resolveRelative(current: FullSlug, target: FullSlug | SimpleSlug): RelativeURL {\n const res = joinSegments(pathToRoot(current), simplifySlug(target as FullSlug)) as RelativeURL;\n return res;\n}\n\nexport function splitAnchor(link: string): [string, string] {\n const [fp, anchor] = link.split(\"#\", 2);\n if (fp!.endsWith(\".pdf\")) {\n return [fp!, anchor === undefined ? \"\" : `#${anchor}`];\n }\n const slugged = anchor === undefined ? \"\" : \"#\" + slugAnchor(anchor);\n return [fp!, slugged];\n}\n\nexport function slugTag(tag: string): string {\n return tag\n .split(\"/\")\n .map((tagSegment) => _sluggify(tagSegment))\n .join(\"/\");\n}\n\nexport function transformInternalLink(link: string): RelativeURL {\n const [fplike, anchor] = splitAnchor(decodeURI(link));\n\n const folderPath = isFolderPath(fplike);\n const segments = fplike.split(\"/\").filter((x) => x.length > 0);\n const prefix = segments.filter(_isRelativeSegment).join(\"/\");\n const fp = segments.filter((seg) => !_isRelativeSegment(seg) && seg !== \"\").join(\"/\");\n\n const simpleSlug = simplifySlug(slugifyFilePath(fp as FilePath));\n const joined = joinSegments(stripSlashes(prefix), stripSlashes(simpleSlug));\n const trail = folderPath ? \"/\" : \"\";\n const res = (_addRelativeToStart(joined) + trail + anchor) as RelativeURL;\n return res;\n}\n\nexport function transformLink(src: FullSlug, target: string, opts: TransformOptions): RelativeURL {\n const targetSlug = transformInternalLink(target);\n\n if (opts.strategy === \"relative\") {\n return targetSlug as RelativeURL;\n } else {\n const folderTail = isFolderPath(targetSlug) ? \"/\" : \"\";\n const canonicalSlug = stripSlashes(targetSlug.slice(\".\".length));\n const [targetCanonical, targetAnchor] = splitAnchor(canonicalSlug);\n\n if (opts.strategy === \"shortest\") {\n const matchingFileNames = opts.allSlugs.filter((slug) => {\n const parts = slug.split(\"/\");\n const fileName = parts.at(-1);\n return targetCanonical === fileName;\n });\n\n if (matchingFileNames.length === 1) {\n const matchedSlug = matchingFileNames[0]!;\n return (resolveRelative(src, matchedSlug) + targetAnchor) as RelativeURL;\n }\n }\n\n return (joinSegments(pathToRoot(src), canonicalSlug) + folderTail) as RelativeURL;\n }\n}\n\nfunction _sluggify(s: string): string {\n return s\n .split(\"/\")\n .map((segment) =>\n segment\n .replace(/\\s/g, \"-\")\n .replace(/&/g, \"-and-\")\n .replace(/%/g, \"-percent\")\n .replace(/\\?/g, \"\")\n .replace(/#/g, \"\"),\n )\n .join(\"/\")\n .replace(/\\/$/, \"\");\n}\n\nfunction _containsForbiddenCharacters(s: string): boolean {\n return s.includes(\" \") || s.includes(\"#\") || s.includes(\"?\") || s.includes(\"&\");\n}\n\nfunction _hasFileExtension(s: string): boolean {\n return getFileExtension(s) !== undefined;\n}\n\nfunction _isRelativeSegment(s: string): boolean {\n return /^\\.{0,2}$/.test(s);\n}\n\nfunction _addRelativeToStart(s: string): string {\n if (s === \"\") {\n s = \".\";\n }\n\n if (!s.startsWith(\".\")) {\n s = joinSegments(\".\", s);\n }\n\n return s;\n}\n","export function removeAllChildren(el: HTMLElement): void {\n while (el.firstChild) {\n el.removeChild(el.firstChild);\n }\n}\n\nexport function registerEscapeHandler(\n outsideContainer: HTMLElement | null,\n onEscape: () => void,\n): () => void {\n if (!outsideContainer) return () => {};\n\n const onClick = (e: MouseEvent) => {\n if (!outsideContainer.classList.contains(\"active\")) {\n return;\n }\n if (e.target === outsideContainer) {\n e.preventDefault();\n e.stopPropagation();\n onEscape();\n }\n };\n\n const onKeydown = (e: KeyboardEvent) => {\n if (!outsideContainer.classList.contains(\"active\")) {\n return;\n }\n if (e.key === \"Escape\") {\n e.preventDefault();\n onEscape();\n }\n };\n\n outsideContainer.addEventListener(\"click\", onClick);\n document.addEventListener(\"keydown\", onKeydown);\n\n return () => {\n outsideContainer.removeEventListener(\"click\", onClick);\n document.removeEventListener(\"keydown\", onKeydown);\n };\n}\n\nexport function normalizeRelativeURLs(html: Document, baseUrl: string): void {\n const elements = html.querySelectorAll(\"[src], [href]\");\n for (const el of Array.from(elements)) {\n const attr = el.hasAttribute(\"href\") ? \"href\" : \"src\";\n const val = el.getAttribute(attr);\n if (!val) continue;\n if (\n val.startsWith(\"http://\") ||\n val.startsWith(\"https://\") ||\n val.startsWith(\"mailto:\") ||\n val.startsWith(\"tel:\") ||\n val.startsWith(\"#\") ||\n val.startsWith(\"/\") ||\n val.startsWith(\"data:\")\n ) {\n continue;\n }\n try {\n const normalized = new URL(val, baseUrl).toString();\n el.setAttribute(attr, normalized);\n } catch {\n continue;\n }\n }\n}\n","export function capitalize(s: string): string {\n return s.substring(0, 1).toUpperCase() + s.substring(1);\n}\n\nexport function classNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n","/**\n * HTML escape utilities for safe string rendering.\n */\n\n/**\n * Escapes HTML special characters in a string to prevent XSS.\n * @param unsafe - The string to escape\n * @returns The escaped string safe for HTML rendering\n *\n * @example\n * escapeHTML('') // '<script>alert("xss")</script>'\n */\nexport function escapeHTML(unsafe: string): string {\n return unsafe\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\n/**\n * Unescapes HTML entities back to their original characters.\n * @param html - The HTML-escaped string\n * @returns The unescaped string\n *\n * @example\n * unescapeHTML('<div>') // '
'\n */\nexport function unescapeHTML(html: string): string {\n return html\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\""\", '\"')\n .replaceAll(\"'\", \"'\");\n}\n","import type { Components, Jsx } from \"hast-util-to-jsx-runtime\";\nimport { toJsxRuntime } from \"hast-util-to-jsx-runtime\";\nimport type { Root, Node } from \"hast\";\nimport { Fragment, jsx, jsxs } from \"preact/jsx-runtime\";\nimport { h } from \"preact\";\n\n/**\n * Extract text content from JSX children.\n *\n * `toJsxRuntime` passes the inner text of `