move type-link processing to build stage

This commit is contained in:
outfoxxed 2024-10-09 01:50:21 -07:00
parent ab2a9ca7ed
commit 6d353e0c6b
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
15 changed files with 161 additions and 300 deletions

View file

@ -1,10 +1,9 @@
import { defineConfig } from "astro/config";
import solidJs from "@astrojs/solid-js";
import { remarkAlert } from "remark-github-blockquote-alert";
import sectionize from "@hbsnow/rehype-sectionize";
import mdx from "@astrojs/mdx";
import pagefind from "./pagefind";
import { markdownConfig } from "./src/config/io/markdown.ts";
// https://astro.build/config
export default defineConfig({
@ -15,27 +14,5 @@ export default defineConfig({
mdx(),
pagefind(),
],
markdown: {
syntaxHighlight: "shiki",
shikiConfig: {
theme: "material-theme-ocean",
wrap: true,
},
remarkPlugins: [
[
remarkAlert,
{
legacyTitle: true,
},
],
],
rehypePlugins: [
[
sectionize,
{
idPropertyName: "id",
},
],
],
},
markdown: markdownConfig,
});

View file

@ -12,28 +12,27 @@
"dependencies": {
"@ark-ui/solid": "^3.5.0",
"@astrojs/check": "^0.9.3",
"@astrojs/markdown-remark": "^5.1.0",
"@astrojs/mdx": "^3.1.7",
"@astrojs/solid-js": "^4.4.2",
"@hbsnow/rehype-sectionize": "^1.0.7",
"@pagefind/default-ui": "^1.1.1",
"@types/node": "^20.14.11",
"astro": "^4.15.9",
"astro-breadcrumbs": "^2.3.1",
"hast-util-from-html": "^2.0.3",
"node": "npm:22.7.0",
"rehype-stringify": "^10.0.1",
"remark-github-blockquote-alert": "^1.2.1",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1",
"solid-devtools": "^0.30.1",
"solid-js": "^1.8.18",
"typescript": "^5.5.3",
"unified": "^11.0.5"
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@astrojs/ts-plugin": "^1.10.2",
"@biomejs/biome": "^1.8.3",
"@hbsnow/rehype-sectionize": "^1.0.7",
"pagefind": "^1.1.1",
"shiki": "^1.11.0"
"pagefind": "^1.1.1"
},
"packageManager": "yarn@4.5.0"
}

View file

@ -1,107 +0,0 @@
---
---
<script>
const qtRegExp = /QT_(\w+)/g
const qsRegExp = /QS_(\w+)/g
setTimeout(() =>{
const blocks = document.querySelectorAll("pre")
if (blocks.length > 0) {
blocks.forEach((block) => {
const content = block.textContent
const elements = block.querySelectorAll("span")
const classElements:HTMLSpanElement[] = [];
if (elements.length === 0) {
console.log("NO SPAN ELEMENTS FOUND")
}
elements.forEach(element => {
const isClassColored = element.style.cssText === "color: rgb(255, 203, 107);"
const isSignal = element.innerText.trim().startsWith("on")
const qualifier = isClassColored && !isSignal
if (qualifier) {
const dotSibling = element.nextSibling
const isSplit = dotSibling?.textContent === "."
if (isSplit) {
let newInnerText = element.innerText + dotSibling.textContent + dotSibling.nextSibling?.textContent
if (dotSibling.nextSibling) {
dotSibling.nextSibling.textContent !== " {"
? dotSibling.nextSibling.remove()
: null
}
dotSibling.remove()
element.innerText = newInnerText
}
classElements.push(element)
}
})
if (content) {
const qtMatch = [...content.matchAll(qtRegExp)]
const qsMatch = [...content.matchAll(qsRegExp)]
if (qtMatch.length > 0) {
for (const qtMatching of qtMatch) {
const newATag = document.createElement("a")
const qtBelongs = qtMatching[0].split("_")[1].replace("11", "-") || null
const qtClass = qtMatching[1].split("_")[1]
const link = `https://doc.qt.io/qt-6/qml-${qtBelongs ? `${qtBelongs}-${qtClass.toLowerCase()}` : "qtquick-" + qtClass.toLowerCase()}.html`
newATag.target = "_blank"
newATag.href = link
newATag.innerText = qtClass
const homeElement = classElements.find(item => {
const spacing = item.innerText.replace(qtMatching[0], "")
if (item.innerText.trim() === qtMatching[0].trim()){
newATag.innerText = spacing + qtClass
return true
}
})
if (homeElement) {
homeElement.innerText = ""
homeElement.appendChild(newATag)
}
}
}
if (qsMatch.length > 0) {
for (const qsMatching of qsMatch) {
const newATag = document.createElement("a")
const qsBelongs = qsMatching[0].split("_")[1].replace("00", ".") || null
const qsClass = qsMatching[1].split("_")[1]
const link = `/docs/types/${qsBelongs ? `${qsBelongs}/${qsClass}` : qsClass}`
newATag.target = "_blank"
newATag.href = link
const homeElement = classElements.find(item => {
const existingItem = item.innerText.trim()
const matchingItem = qsMatching[0].trim()
const spacing = item.innerText.replace(existingItem, "")
if (existingItem === matchingItem) {
newATag.innerText = spacing + qsClass
return true
}
})
if (homeElement) {
homeElement.innerText = ""
if (homeElement.nextSibling) {
homeElement.nextSibling.textContent !== " {"
? homeElement.nextSibling.textContent = ""
: null
}
homeElement.appendChild(newATag)
}
}
}
}
});
}
},3000)
</script>

View file

@ -1,66 +0,0 @@
---
---
<script>
import { getQMLTypeLinkObject, getQMLTypeLink, getIconForLink } from "@config/io/helpers"
const detailsData = document.getElementsByTagName("p")
const innerItems = document.getElementsByClassName("typedata-details")
if (detailsData) {
for (const details of detailsData) {
const linkRegex = /TYPE99(\w+.)99TYPE/g
const mtypeExists = details.textContent?.match(linkRegex)
if (!mtypeExists || !details.textContent) {
continue;
}
const linkMatch = [...details.textContent.matchAll(linkRegex)]
let textWithLinks = details.textContent;
for (const matching of linkMatch) {
if (details.textContent.indexOf(matching[0]) === -1){
continue
}
const linkObject = getQMLTypeLinkObject(matching[1]);
const link = getQMLTypeLink(linkObject);
const icon = linkObject.mtype ? getIconForLink(linkObject.mtype, false) : null;
// for signal
const bracketString = getIconForLink("func", false)
const newLink = `<span class="type${linkObject.mtype}-link typedata-link">${icon ? icon : ""}<a href=${link}>${linkObject.mname || linkObject.name}</a>${linkObject.mtype === "signal" ? bracketString : ""}</span>`;
textWithLinks = textWithLinks.replace(matching[0], newLink)
}
details.innerHTML = textWithLinks
}
}
if (innerItems){
for (const innerItem of innerItems){
const linkRegex = /TYPE99(\w+.)99TYPE/g
const listItems = innerItem.getElementsByTagName("li")
for (const li of listItems){
const mtypeExists = li.textContent?.match(linkRegex)
if (!mtypeExists || !li.textContent){
continue
}
const linkMatch = [...li.textContent.matchAll(linkRegex)]
let textWithLinks = li.textContent;
for (const matching of linkMatch) {
if (li.textContent.indexOf(matching[0]) === -1){
continue
}
const linkObject = getQMLTypeLinkObject(matching[1]);
const link = getQMLTypeLink(linkObject);
const icon = linkObject.mtype ? getIconForLink(linkObject.mtype, false) : null;
// for signal
const bracketString = getIconForLink("func", false)
const newLink = `<span class="type${linkObject.mtype}-link typedata-link">${icon ? icon : ""}<a href=${link}>${linkObject.mname || linkObject.name}</a>${linkObject.mtype === "signal" ? bracketString : ""}</span>`;
textWithLinks = textWithLinks.replace(matching[0], newLink)
}
li.innerHTML = textWithLinks
}
}
}
</script>

View file

@ -1,21 +0,0 @@
---
---
<script>
import { codeToHtml } from "shiki";
const injectedMd = document.getElementById("injectedMd");
if (injectedMd) {
const preElements = document.getElementsByTagName("pre");
for (const pre of preElements) {
if (pre.textContent){
const innerText = pre.innerText;
pre.outerHTML =
await codeToHtml(
innerText,
{
lang: "qml", "theme":"material-theme-ocean"
}
);
}
}
}
</script>

View file

@ -3,18 +3,15 @@ import type {
QMLTypeLinkObject,
QuickshellFunction,
} from "@config/io/types";
import {
parseMarkdown,
getQMLTypeLink,
} from "@config/io/helpers";
import { getQMLTypeLink } from "@config/io/helpers";
import { Tag } from "@icons";
import TypeDetails from "./TypeDetails.astro"
export interface Props {
funcData: QuickshellFunction[];
title: string;
}
const { funcData, title } = Astro.props;
const { funcData } = Astro.props;
---
<ul class="typedata typefuncs">
{
@ -51,9 +48,7 @@ const { funcData, title } = Astro.props;
)
:null
}
<section class="typedata-details">
<div class="typedata-detailsdata" set:html={parseMarkdown(item.details, title)}/>
</section>
<TypeDetails markdown={item.details} />
</li>
)
})

View file

@ -1,21 +1,19 @@
---
import {
parseMarkdown,
getQMLTypeLink,
} from "@config/io/helpers";
import { getQMLTypeLink } from "@config/io/helpers";
import type {
QMLTypeLinkObject,
QuickshellProps,
} from "@config/io/types";
import { Tag, Flag } from "@icons";
import TypeDetails from "./TypeDetails.astro"
export interface Props {
propsKeys: string[];
propsData: QuickshellProps;
title: string;
}
const { propsKeys, propsData, title } = Astro.props;
const { propsKeys, propsData } = Astro.props;
---
<ul class="typedata typeprops">
{
@ -73,9 +71,7 @@ const { propsKeys, propsData, title } = Astro.props;
</p>
):null
}
<section class="typedata-details">
<div id="injectedMd" class="typedata-detailsdata" set:html={parseMarkdown(propData.details, title)} />
</section>
<TypeDetails markdown={propData.details} />
</li>
)
})

View file

@ -1,15 +1,14 @@
---
import type { QuickshellSignal } from "@config/io/types";
import { Tag, PowerCord } from "@icons";
import { parseMarkdown } from "@config/io/helpers";
import TypeDetails from "./TypeDetails.astro";
export interface Props {
signalKeys: string[];
signalsData: QuickshellSignal;
title: string;
}
const { signalKeys, signalsData, title } = Astro.props;
const { signalKeys, signalsData } = Astro.props;
---
<ul class="typedata typesignals">
{
@ -44,9 +43,7 @@ const { signalKeys, signalsData, title } = Astro.props;
</p>
) : null
}
<section class="typedata-details">
<div class="typedata-detailsdata" set:html={parseMarkdown(signalData.details, title)} />
</section>
<TypeDetails markdown={signalData.details} />
</li>
)
})

View file

@ -0,0 +1,14 @@
---
import { processQsMarkdown } from "@config/io/helpers"
export interface Props {
markdown?: string,
}
const { markdown } = Astro.props;
const html = markdown ? await processQsMarkdown(markdown) : null;
---
<section class="typedata-details">
{html ? <div class="typedata-detailsdata" set:html={html} /> : <em>No details provided</em>}
</section>

View file

@ -1,15 +1,14 @@
---
import type { QuickshellVariant } from "@config/io/types";
import { FourDiamonds } from "../icons";
import { parseMarkdown } from "@src/config/io/helpers";
import TypeDetails from "./TypeDetails.astro";
export interface Props {
variantKeys: string[];
variantsData: QuickshellVariant;
title: string;
}
const { variantKeys, variantsData, title } = Astro.props;
const { variantKeys, variantsData } = Astro.props;
---
<ul class="typedata typevariants">
{
@ -34,9 +33,7 @@ const { variantKeys, variantsData, title } = Astro.props;
)
:null
}
<section class="typedata-details">
<div class="typedata-detailsdata" set:html={parseMarkdown(variantData.details, title)} />
</section>
<TypeDetails markdown={variantData.details} />
</li>
)
})

View file

@ -1,8 +1,4 @@
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import { remarkAlert } from "remark-github-blockquote-alert";
import * as markdownUtils from "./markdown.ts";
import {
// Flag,
@ -10,7 +6,8 @@ import {
Tag,
FourDiamonds,
RoundBrackets,
} from "@icons";
// @icons breaks when imported indirectly from astro.config.mjs
} from "../../components/icons.tsx";
import type {
ConfigHeading,
ConfigTOC,
@ -78,21 +75,8 @@ export function groupRoutes(routes: RouteData[]): GroupedRoutes {
}, defaultValue);
}
export function parseMarkdown(text?: string, title?: string) {
if (!text) {
return unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.process(title);
}
return unified()
.use(remarkParse)
.use(remarkAlert)
.use(remarkRehype)
.use(rehypeStringify)
.process(text);
export async function processQsMarkdown(markdown: string): Promise<string> {
return await markdownUtils.processMarkdown(markdown);
}
export function getQMLTypeLinkObject(unparsed: string) {

108
src/config/io/markdown.ts Normal file
View file

@ -0,0 +1,108 @@
import type { Transformer } from "unified";
import type { Node, Parent } from "unist";
import { visit, CONTINUE, SKIP } from "unist-util-visit";
import { fromHtml } from "hast-util-from-html";
import type { AstroMarkdownOptions, MarkdownProcessor } from "@astrojs/markdown-remark";
import { createMarkdownProcessor } from "@astrojs/markdown-remark";
import { remarkAlert } from "remark-github-blockquote-alert";
import sectionize from "@hbsnow/rehype-sectionize";
import { getQMLTypeLinkObject, getQMLTypeLink, getIconForLink } from "./helpers.ts"
// couldn't find the actual type to use
type HtmlNode = Node & {
value: string,
type: string,
tagName: string,
};
function rehypeRewriteTypelinks(): Transformer {
return function (root: Node): Node {
// @ts-ignore: does not appear to be a way to give this a correct type, it works for the one with a test below
visit(root, (node: HtmlNode, index: number, parent: Parent) => {
if (node.type == "element" && node.tagName == "pre") return SKIP;
if (node.type == "text") {
let changed = false;
node.value = node.value.replace(/TYPE99(\w+.)99TYPE/g, (_full: string, match: string) => {
changed = true;
const linkObject = getQMLTypeLinkObject(match);
const link = getQMLTypeLink(linkObject);
const icon = (linkObject.mtype && linkObject.mtype != "func") ? getIconForLink(linkObject.mtype, false) : null;
const hasParens = linkObject.mtype == "func" || linkObject.mtype == "signal";
const hasDot = linkObject.name && linkObject.mname;
return `<a class="type${linkObject.mtype}-link typedata-link" href=${link}>${icon ?? ""}${linkObject.name ?? ""}${hasDot ? "." : ""}${linkObject.mname ?? ""}${hasParens ? "()" : ""}</a>`;
});
if (changed) {
const fragment = fromHtml(node.value, { fragment: true });
parent.children.splice(index, 1, ...fragment.children);
return SKIP;
}
}
return CONTINUE;
});
return root;
}
}
const shikiRewriteTypelinks = {
code: (root: Node) => {
visit(root, "text", (node: HtmlNode, index: number, parent: Parent) => {
let changed = false;
node.value = node.value.replace(/TYPE99(\w+.)99TYPE/g, (_full: string, match: string) => {
changed = true;
const linkObject = getQMLTypeLinkObject(match);
const link = getQMLTypeLink(linkObject);
return `<a href=${link}>${linkObject.name ?? ""}</a>`;
});
if (changed) {
const fragment = fromHtml(node.value, { fragment: true });
parent.children.splice(index, 1, ...fragment.children);
}
});
}
};
export const markdownConfig: AstroMarkdownOptions = {
syntaxHighlight: "shiki",
shikiConfig: {
theme: "material-theme-ocean",
wrap: true,
transformers: [ shikiRewriteTypelinks ],
},
remarkPlugins: [[
remarkAlert,
{ legacyTitle: true },
]],
rehypePlugins: [
[ sectionize, { idPropertyName: "id" } ],
// @ts-ignore: can't tell what this wants
[ rehypeRewriteTypelinks, {} ],
],
};
let globalMarkdownProcessor: Promise<MarkdownProcessor>;
async function getMarkdownProcessor(): Promise<MarkdownProcessor> {
if (!globalMarkdownProcessor) {
globalMarkdownProcessor = createMarkdownProcessor(markdownConfig);
}
return globalMarkdownProcessor;
}
export async function processMarkdown(markdown: string): Promise<string> {
return (await (await getMarkdownProcessor()).render(markdown)).code;
}

View file

@ -7,7 +7,6 @@ import PreTheme from "@config/PreTheme.astro";
import Header from "@components/Header.astro";
import Head from "@config/Head.astro";
import Nav from "@components/navigation/sidebars/Nav.astro";
import CreateQMLCodeButtons from "@components/hooks/CreateQMLCodeButtons.astro";
import "@styles/global.css";
import type { ConfigHeading } from "@src/components/navigation/sidebars/types";
import Footer from "@src/components/Footer.astro";
@ -63,7 +62,6 @@ if (url[2]) {
<link rel="canonical" href={Astro.url} />
<PreTheme />
<CreateCopyButtons />
<CreateQMLCodeButtons/>
</head>
<body class="docslayout">
<Header headings={headings}/>

View file

@ -1,6 +1,6 @@
---
import {
parseMarkdown,
processQsMarkdown,
getQMLTypeLink,
} from "@config/io/helpers";
import { generateTypeData } from "@config/io/generateTypeData";
@ -11,8 +11,6 @@ import Properties from "@components/type/Properties.astro";
import Functions from "@components/type/Functions.astro";
import Signals from "@components/type/Signals.astro";
import Variants from "@components/type/Variants.astro";
import TransformMDCodeblocks from "@components/hooks/TransformMDCodeblocks.astro";
import TransformLinks from "@components/hooks/TransformLinks.astro";
export async function getStaticPaths() {
const routes = await generateTypeData();
@ -51,11 +49,8 @@ const sidebarData = {
const superLink = data.super ? getQMLTypeLink(data.super) : null;
const details = parseMarkdown(data.details, route.name);
const details = data.details ? await processQsMarkdown(data.details) : null;
---
<TransformLinks/>
<TransformMDCodeblocks/>
<DocsLayout title={`${route.name} - ${route.type}`} description={data?.description ?? ""}>
<div class="docs">
<div class="typedocs-content">
@ -87,34 +82,30 @@ const details = parseMarkdown(data.details, route.name);
</span>
))}</div>
):null}
<span id="injectedMd" set:html={details}/>
{details ? <span set:html={details}/> : null}
{!details ? (<span class="toparse">{data.description}</span>):null}
</subheading>
{ data.properties && propsKeys ? (
<Properties
propsData={data.properties}
propsKeys={propsKeys!}
title={route.name}
/>
): null}
{ data.functions && data.functions.length > 0 ? (
<Functions
funcData={data.functions}
title={route.name}
/>
): null}
{ data.signals && signalKeys ? (
<Signals
signalsData={data.signals}
signalKeys={signalKeys}
title={route.name}
/>
):null}
{ data.variants && variantKeys ? (
<Variants
variantsData={data.variants}
variantKeys={variantKeys}
title={route.name}
/>
):null}
</section>

View file

@ -138,7 +138,7 @@ __metadata:
languageName: node
linkType: hard
"@astrojs/markdown-remark@npm:5.2.0":
"@astrojs/markdown-remark@npm:5.2.0, @astrojs/markdown-remark@npm:^5.1.0":
version: 5.2.0
resolution: "@astrojs/markdown-remark@npm:5.2.0"
dependencies:
@ -4081,7 +4081,7 @@ __metadata:
languageName: node
linkType: hard
"hast-util-from-html@npm:^2.0.0, hast-util-from-html@npm:^2.0.1":
"hast-util-from-html@npm:^2.0.0, hast-util-from-html@npm:^2.0.1, hast-util-from-html@npm:^2.0.3":
version: 2.0.3
resolution: "hast-util-from-html@npm:2.0.3"
dependencies:
@ -6302,6 +6302,7 @@ __metadata:
dependencies:
"@ark-ui/solid": "npm:^3.5.0"
"@astrojs/check": "npm:^0.9.3"
"@astrojs/markdown-remark": "npm:^5.1.0"
"@astrojs/mdx": "npm:^3.1.7"
"@astrojs/solid-js": "npm:^4.4.2"
"@astrojs/ts-plugin": "npm:^1.10.2"
@ -6311,17 +6312,15 @@ __metadata:
"@types/node": "npm:^20.14.11"
astro: "npm:^4.15.9"
astro-breadcrumbs: "npm:^2.3.1"
hast-util-from-html: "npm:^2.0.3"
node: "npm:22.7.0"
pagefind: "npm:^1.1.1"
rehype-stringify: "npm:^10.0.1"
remark-github-blockquote-alert: "npm:^1.2.1"
remark-parse: "npm:^11.0.0"
remark-rehype: "npm:^11.1.1"
shiki: "npm:^1.11.0"
solid-devtools: "npm:^0.30.1"
solid-js: "npm:^1.8.18"
typescript: "npm:^5.5.3"
unified: "npm:^11.0.5"
unist-util-visit: "npm:^5.0.0"
languageName: unknown
linkType: soft
@ -6382,7 +6381,7 @@ __metadata:
languageName: node
linkType: hard
"rehype-stringify@npm:^10.0.0, rehype-stringify@npm:^10.0.1":
"rehype-stringify@npm:^10.0.0":
version: 10.0.1
resolution: "rehype-stringify@npm:10.0.1"
dependencies:
@ -6473,7 +6472,7 @@ __metadata:
languageName: node
linkType: hard
"remark-rehype@npm:^11.0.0, remark-rehype@npm:^11.1.0, remark-rehype@npm:^11.1.1":
"remark-rehype@npm:^11.0.0, remark-rehype@npm:^11.1.0":
version: 11.1.1
resolution: "remark-rehype@npm:11.1.1"
dependencies:
@ -6830,7 +6829,7 @@ __metadata:
languageName: node
linkType: hard
"shiki@npm:^1.10.3, shiki@npm:^1.11.0, shiki@npm:^1.16.2":
"shiki@npm:^1.10.3, shiki@npm:^1.16.2":
version: 1.20.0
resolution: "shiki@npm:1.20.0"
dependencies: