chore: commit dist/ and remove prepare script

With build tools (tsup, typescript) moved to devDependencies, the prepare
script can no longer build when npm fetches this package from GitHub (npm
does not install devDependencies for git dependencies). Commit the pre-built
dist/ output so GitHub-based installs work without a build step.
This commit is contained in:
saberzero1 2026-03-14 21:30:17 +01:00
parent 61cbfb9807
commit 81189f697c
No known key found for this signature in database
20 changed files with 682 additions and 2 deletions

2
.gitignore vendored
View file

@ -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

5
dist/dom.d.ts vendored Normal file
View file

@ -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 };

56
dist/dom.js vendored Normal file
View file

@ -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

1
dist/dom.js.map vendored Normal file
View file

@ -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<HTMLElement>(\"[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"]}

23
dist/escape.d.ts vendored Normal file
View file

@ -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>') // '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'
*/
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('&lt;div&gt;') // '<div>'
*/
declare function unescapeHTML(html: string): string;
export { escapeHTML, unescapeHTML };

11
dist/escape.js vendored Normal file
View file

@ -0,0 +1,11 @@
// src/escape.ts
function escapeHTML(unsafe) {
return unsafe.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#039;");
}
function unescapeHTML(html) {
return html.replaceAll("&amp;", "&").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", '"').replaceAll("&#039;", "'");
}
export { escapeHTML, unescapeHTML };
//# sourceMappingURL=escape.js.map
//# sourceMappingURL=escape.js.map

1
dist/escape.js.map vendored Normal file
View file

@ -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>') // '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'\n */\nexport function escapeHTML(unsafe: string): string {\n return unsafe\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\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('&lt;div&gt;') // '<div>'\n */\nexport function unescapeHTML(html: string): string {\n return html\n .replaceAll(\"&amp;\", \"&\")\n .replaceAll(\"&lt;\", \"<\")\n .replaceAll(\"&gt;\", \">\")\n .replaceAll(\"&quot;\", '\"')\n .replaceAll(\"&#039;\", \"'\");\n}\n"]}

8
dist/index.d.ts vendored Normal file
View file

@ -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';

280
dist/index.js vendored Normal file
View file

@ -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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#039;");
}
function unescapeHTML(html) {
return html.replaceAll("&amp;", "&").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", '"').replaceAll("&#039;", "'");
}
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

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

20
dist/jsx.d.ts vendored Normal file
View file

@ -0,0 +1,20 @@
import { Components } from 'hast-util-to-jsx-runtime';
import { Node } from 'hast';
/**
* Convert a HAST tree to Preact JSX, with sensible defaults for Quartz plugins.
*
* This is the canonical way for pageType body components (and any other code
* that renders a HAST tree to JSX) to produce output that is safe for
* `preact-render-to-string`. It applies built-in component overrides for
* `<table>`, `<style>`, and `<script>` elements, preventing the common
* pitfall where inline CSS / JS gets HTML-escaped.
*
* @param tree The HAST root (or node) to render.
* @param components Optional additional component overrides. These are
* merged with (and override) the built-in defaults.
* @returns A Preact JSX element ready for `render()`.
*/
declare function htmlToJsx(tree: Node, components?: Partial<Components>): JSX.Element;
export { htmlToJsx };

28
dist/jsx.js vendored Normal file
View file

@ -0,0 +1,28 @@
import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
import { jsxs, jsx, Fragment } from 'preact/jsx-runtime';
import { h } from 'preact';
// src/jsx.tsx
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 { htmlToJsx };
//# sourceMappingURL=jsx.js.map
//# sourceMappingURL=jsx.js.map

1
dist/jsx.js.map vendored Normal file
View file

@ -0,0 +1 @@
{"version":3,"sources":["../src/jsx.tsx"],"names":["jsx"],"mappings":";;;;;AAcA,SAAS,iBAAiB,QAAA,EAA2B;AACnD,EAAA,IAAI,OAAO,QAAA,KAAa,QAAA,EAAU,OAAO,QAAA;AACzC,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG,OAAO,SAAS,GAAA,CAAI,gBAAgB,CAAA,CAAE,IAAA,CAAK,EAAE,CAAA;AAC1E,EAAA,OAAO,MAAA,CAAO,YAAY,EAAE,CAAA;AAC9B;AASA,IAAM,iBAAA,GAAgC;AAAA,EACpC,KAAA,EAAO,CAAC,KAAA,qBACNA,GAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,iBAAA,EACT,QAAA,kBAAAA,GAAAA,CAAC,OAAA,EAAA,EAAO,GAAG,OAAO,CAAA,EACpB,CAAA;AAAA,EAEF,OAAO,CAAC,EAAE,UAAU,GAAG,IAAA,OACrB,CAAA,CAAE,OAAA,EAAS,EAAE,GAAG,IAAA,EAAM,yBAAyB,EAAE,MAAA,EAAQ,iBAAiB,QAAQ,CAAA,IAAK,CAAA;AAAA,EACzF,QAAQ,CAAC,EAAE,UAAU,GAAG,IAAA,OACtB,CAAA,CAAE,QAAA,EAAU,EAAE,GAAG,IAAA,EAAM,yBAAyB,EAAE,MAAA,EAAQ,iBAAiB,QAAQ,CAAA,IAAK;AAC5F,CAAA;AAgBO,SAAS,SAAA,CAAU,MAAY,UAAA,EAAkC;AACtE,EAAA,OAAO,aAAa,IAAA,EAAc;AAAA,IAChC,QAAA;AAAA,IACA,GAAA;AAAA,IACA,IAAA;AAAA,IACA,wBAAA,EAA0B,MAAA;AAAA,IAC1B,UAAA,EAAY,EAAE,GAAG,iBAAA,EAAmB,GAAG,UAAA;AAAW,GACnD,CAAA;AACH","file":"jsx.js","sourcesContent":["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 `<style>` / `<script>` elements as\n * the `children` prop. This helper normalises the various shapes it can take\n * (string, array of strings, or other) into a single string so we can feed it\n * to `dangerouslySetInnerHTML`.\n */\nfunction childrenToString(children: unknown): string {\n if (typeof children === \"string\") return children;\n if (Array.isArray(children)) return children.map(childrenToString).join(\"\");\n return String(children ?? \"\");\n}\n\n/**\n * Default component overrides applied by {@link htmlToJsx}.\n *\n * - **table** wrapped in a scrollable `<div class=\"table-container\">`\n * - **style** / **script** rendered with `dangerouslySetInnerHTML` so that\n * special characters (`\"`, `&`, etc.) are *not* HTML-escaped by preact.\n */\nconst builtinComponents: Components = {\n table: (props) => (\n <div class=\"table-container\">\n <table {...props} />\n </div>\n ),\n style: ({ children, ...rest }) =>\n h(\"style\", { ...rest, dangerouslySetInnerHTML: { __html: childrenToString(children) } }),\n script: ({ children, ...rest }) =>\n h(\"script\", { ...rest, dangerouslySetInnerHTML: { __html: childrenToString(children) } }),\n};\n\n/**\n * Convert a HAST tree to Preact JSX, with sensible defaults for Quartz plugins.\n *\n * This is the canonical way for pageType body components (and any other code\n * that renders a HAST tree to JSX) to produce output that is safe for\n * `preact-render-to-string`. It applies built-in component overrides for\n * `<table>`, `<style>`, and `<script>` elements, preventing the common\n * pitfall where inline CSS / JS gets HTML-escaped.\n *\n * @param tree The HAST root (or node) to render.\n * @param components Optional additional component overrides. These are\n * merged with (and override) the built-in defaults.\n * @returns A Preact JSX element ready for `render()`.\n */\nexport function htmlToJsx(tree: Node, components?: Partial<Components>) {\n return toJsxRuntime(tree as Root, {\n Fragment,\n jsx: jsx as Jsx,\n jsxs: jsxs as Jsx,\n elementAttributeNameCase: \"html\",\n components: { ...builtinComponents, ...components },\n });\n}\n"]}

4
dist/lang.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare function capitalize(s: string): string;
declare function classNames(...classes: (string | undefined | null | false)[]): string;
export { capitalize, classNames };

11
dist/lang.js vendored Normal file
View file

@ -0,0 +1,11 @@
// src/lang.ts
function capitalize(s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
export { capitalize, classNames };
//# sourceMappingURL=lang.js.map
//# sourceMappingURL=lang.js.map

1
dist/lang.js.map vendored Normal file
View file

@ -0,0 +1 @@
{"version":3,"sources":["../src/lang.ts"],"names":[],"mappings":";AAAO,SAAS,WAAW,CAAA,EAAmB;AAC5C,EAAA,OAAO,CAAA,CAAE,UAAU,CAAA,EAAG,CAAC,EAAE,WAAA,EAAY,GAAI,CAAA,CAAE,SAAA,CAAU,CAAC,CAAA;AACxD;AAEO,SAAS,cAAc,OAAA,EAAwD;AACpF,EAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,GAAG,CAAA;AACzC","file":"lang.js","sourcesContent":["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"]}

40
dist/path.d.ts vendored Normal file
View file

@ -0,0 +1,40 @@
import { FullSlug, FilePath } from '@quartz-community/types';
export { FilePath, FullSlug } from '@quartz-community/types';
/** No '/index' ending, no file extension, can have trailing slash for folders. */
type SimpleSlug = string & {
_brand: "SimpleSlug";
};
/** Starts with './' or '../', used for navigation. */
type RelativeURL = string & {
_brand: "RelativeURL";
};
interface TransformOptions {
strategy: "absolute" | "relative" | "shortest";
allSlugs: FullSlug[];
}
declare function isFilePath(s: string): s is FilePath;
declare function isFullSlug(s: string): s is FullSlug;
declare function isSimpleSlug(s: string): s is SimpleSlug;
declare function isRelativeURL(s: string): s is RelativeURL;
declare function isAbsoluteURL(s: string): boolean;
declare function simplifySlug(fp: FullSlug | string): SimpleSlug;
declare function getFullSlug(window: Window): FullSlug;
declare function getFullSlugFromUrl(): FullSlug;
declare function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug;
declare function joinSegments(...args: string[]): string;
declare function resolvePath(to: string): string;
declare function endsWith(s: string, suffix: string): boolean;
declare function trimSuffix(s: string, suffix: string): string;
declare function stripSlashes(s: string, onlyStripPrefix?: boolean): string;
declare function getFileExtension(s: string): string | undefined;
declare function isFolderPath(fplike: string): boolean;
declare function getAllSegmentPrefixes(path: string): string[];
declare function pathToRoot(slug: FullSlug): RelativeURL;
declare function resolveRelative(current: FullSlug, target: FullSlug | SimpleSlug): RelativeURL;
declare function splitAnchor(link: string): [string, string];
declare function slugTag(tag: string): string;
declare function transformInternalLink(link: string): RelativeURL;
declare function transformLink(src: FullSlug, target: string, opts: TransformOptions): RelativeURL;
export { type RelativeURL, type SimpleSlug, type TransformOptions, endsWith, getAllSegmentPrefixes, getFileExtension, getFullSlug, getFullSlugFromUrl, isAbsoluteURL, isFilePath, isFolderPath, isFullSlug, isRelativeURL, isSimpleSlug, joinSegments, pathToRoot, resolvePath, resolveRelative, simplifySlug, slugTag, slugifyFilePath, splitAnchor, stripSlashes, transformInternalLink, transformLink, trimSuffix };

189
dist/path.js vendored Normal file
View file

@ -0,0 +1,189 @@
import { slug } from 'github-slugger';
// 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;
}
export { endsWith, getAllSegmentPrefixes, getFileExtension, getFullSlug, getFullSlugFromUrl, isAbsoluteURL, isFilePath, isFolderPath, isFullSlug, isRelativeURL, isSimpleSlug, joinSegments, pathToRoot, resolvePath, resolveRelative, simplifySlug, slugTag, slugifyFilePath, splitAnchor, stripSlashes, transformInternalLink, transformLink, trimSuffix };
//# sourceMappingURL=path.js.map
//# sourceMappingURL=path.js.map

1
dist/path.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -58,7 +58,6 @@
},
"scripts": {
"build": "tsup",
"prepare": "npm run build",
"dev": "tsup --watch",
"lint": "eslint . --max-warnings=0",
"format": "prettier . --check",