fixed markdown and shiki(untested) link transformers
This commit is contained in:
		
							parent
							
								
									6d353e0c6b
								
							
						
					
					
						commit
						4fbb038c0d
					
				
					 4 changed files with 103 additions and 66 deletions
				
			
		| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
    "@types/node": "^20.14.11",
 | 
			
		||||
    "astro": "^4.15.9",
 | 
			
		||||
    "astro-breadcrumbs": "^2.3.1",
 | 
			
		||||
    "hast": "^1.0.0",
 | 
			
		||||
    "hast-util-from-html": "^2.0.3",
 | 
			
		||||
    "node": "npm:22.7.0",
 | 
			
		||||
    "remark-github-blockquote-alert": "^1.2.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,7 +75,9 @@ export function groupRoutes(routes: RouteData[]): GroupedRoutes {
 | 
			
		|||
  }, defaultValue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function processQsMarkdown(markdown: string): Promise<string> {
 | 
			
		||||
export async function processQsMarkdown(
 | 
			
		||||
  markdown: string
 | 
			
		||||
): Promise<string> {
 | 
			
		||||
  return await markdownUtils.processMarkdown(markdown);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,78 +1,102 @@
 | 
			
		|||
import type { Transformer } from "unified";
 | 
			
		||||
import type { Node, Parent } from "unist";
 | 
			
		||||
import type { Parent, Node } 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 type { Root, Element } from "hast";
 | 
			
		||||
import type {
 | 
			
		||||
  AstroMarkdownOptions,
 | 
			
		||||
  MarkdownProcessor,
 | 
			
		||||
  RehypePlugin,
 | 
			
		||||
} from "@astrojs/markdown-remark";
 | 
			
		||||
import { createMarkdownProcessor } from "@astrojs/markdown-remark";
 | 
			
		||||
 | 
			
		||||
import { remarkAlert } from "remark-github-blockquote-alert";
 | 
			
		||||
import sectionize from "@hbsnow/rehype-sectionize";
 | 
			
		||||
import type { ShikiTransformer } from "shiki";
 | 
			
		||||
 | 
			
		||||
import { getQMLTypeLinkObject, getQMLTypeLink, getIconForLink } from "./helpers.ts"
 | 
			
		||||
import {
 | 
			
		||||
  getQMLTypeLinkObject,
 | 
			
		||||
  getQMLTypeLink,
 | 
			
		||||
  getIconForLink,
 | 
			
		||||
} from "./helpers.ts";
 | 
			
		||||
 | 
			
		||||
// couldn't find the actual type to use
 | 
			
		||||
type HtmlNode = Node & {
 | 
			
		||||
  value: string,
 | 
			
		||||
  type: string,
 | 
			
		||||
  tagName: string,
 | 
			
		||||
};
 | 
			
		||||
interface HtmlNode extends 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") {
 | 
			
		||||
const rehypeRewriteTypelinks: RehypePlugin<[]> = () => {
 | 
			
		||||
  return (root: Node): Root => {
 | 
			
		||||
    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) => {
 | 
			
		||||
        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 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 });
 | 
			
		||||
          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;
 | 
			
		||||
    return root as Root;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
      node.value = node.value.replace(/TYPE99(\w+.)99TYPE/g, (_full: string, match: string) => {
 | 
			
		||||
        changed = true;
 | 
			
		||||
const shikiRewriteTypelinks = (): ShikiTransformer => {
 | 
			
		||||
  return {
 | 
			
		||||
    name: "rewrite-typelinks",
 | 
			
		||||
    preprocess(code, _options) {
 | 
			
		||||
      const qtRegExp = /QT_(\w+)/g;
 | 
			
		||||
      const qsRegExp = /QS_(\w+)/g;
 | 
			
		||||
      // WARN: need to change the code link identifier to this
 | 
			
		||||
      // const hasTypelinks = code.search(/TYPE99(\w+.)99TYPE/g) !== -1;
 | 
			
		||||
      const hasQTLink = code.search(qtRegExp) !== -1;
 | 
			
		||||
      const hasQSLink = code.search(qsRegExp) !== -1;
 | 
			
		||||
 | 
			
		||||
      if (hasQTLink) {
 | 
			
		||||
        code.replace(qtRegExp, (_full: string, match: string) => {
 | 
			
		||||
          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);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (hasQSLink) {
 | 
			
		||||
        code.replace(qsRegExp, (_full: string, match: string) => {
 | 
			
		||||
          const linkObject = getQMLTypeLinkObject(match);
 | 
			
		||||
          const link = getQMLTypeLink(linkObject);
 | 
			
		||||
          return `<a href=${link}>${linkObject.name ?? ""}</a>`;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const markdownConfig: AstroMarkdownOptions = {
 | 
			
		||||
| 
						 | 
				
			
			@ -80,16 +104,14 @@ export const markdownConfig: AstroMarkdownOptions = {
 | 
			
		|||
  shikiConfig: {
 | 
			
		||||
    theme: "material-theme-ocean",
 | 
			
		||||
    wrap: true,
 | 
			
		||||
    transformers: [ shikiRewriteTypelinks ],
 | 
			
		||||
    transformers: [shikiRewriteTypelinks()],
 | 
			
		||||
  },
 | 
			
		||||
  remarkPlugins: [[
 | 
			
		||||
    remarkAlert,
 | 
			
		||||
    { legacyTitle: true },
 | 
			
		||||
  ]],
 | 
			
		||||
  remarkPlugins: [[remarkAlert, { legacyTitle: true }]],
 | 
			
		||||
  rehypePlugins: [
 | 
			
		||||
    [ sectionize, { idPropertyName: "id" } ],
 | 
			
		||||
    // @ts-ignore: can't tell what this wants
 | 
			
		||||
    [ rehypeRewriteTypelinks, {} ],
 | 
			
		||||
    // FIXME: incompatible types between unified/Plugin and Astro/RehypePlugin
 | 
			
		||||
    // @ts-expect-error
 | 
			
		||||
    [sectionize as RehypePlugin, { idPropertyName: "id" }],
 | 
			
		||||
    rehypeRewriteTypelinks,
 | 
			
		||||
  ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,12 +119,16 @@ let globalMarkdownProcessor: Promise<MarkdownProcessor>;
 | 
			
		|||
 | 
			
		||||
async function getMarkdownProcessor(): Promise<MarkdownProcessor> {
 | 
			
		||||
  if (!globalMarkdownProcessor) {
 | 
			
		||||
    globalMarkdownProcessor = createMarkdownProcessor(markdownConfig);
 | 
			
		||||
    globalMarkdownProcessor =
 | 
			
		||||
      createMarkdownProcessor(markdownConfig);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return globalMarkdownProcessor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function processMarkdown(markdown: string): Promise<string> {
 | 
			
		||||
  return (await (await getMarkdownProcessor()).render(markdown)).code;
 | 
			
		||||
export async function processMarkdown(
 | 
			
		||||
  markdown: string
 | 
			
		||||
): Promise<string> {
 | 
			
		||||
  return (await (await getMarkdownProcessor()).render(markdown))
 | 
			
		||||
    .code;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4364,6 +4364,13 @@ __metadata:
 | 
			
		|||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"hast@npm:^1.0.0":
 | 
			
		||||
  version: 1.0.0
 | 
			
		||||
  resolution: "hast@npm:1.0.0"
 | 
			
		||||
  checksum: 10c0/a1a4ffaa99c6a4cfe6bd31e54ee919625a8ebcfc621d2d06cc76e593d7aeee4f1f430fcdbbf17ae8e249567ee7b724e4814b5eb273455d23bad8334c9bfc667d
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"hastscript@npm:^7.0.0":
 | 
			
		||||
  version: 7.2.0
 | 
			
		||||
  resolution: "hastscript@npm:7.2.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -6312,6 +6319,7 @@ __metadata:
 | 
			
		|||
    "@types/node": "npm:^20.14.11"
 | 
			
		||||
    astro: "npm:^4.15.9"
 | 
			
		||||
    astro-breadcrumbs: "npm:^2.3.1"
 | 
			
		||||
    hast: "npm:^1.0.0"
 | 
			
		||||
    hast-util-from-html: "npm:^2.0.3"
 | 
			
		||||
    node: "npm:22.7.0"
 | 
			
		||||
    pagefind: "npm:^1.1.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue