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",
 | 
					    "@types/node": "^20.14.11",
 | 
				
			||||||
    "astro": "^4.15.9",
 | 
					    "astro": "^4.15.9",
 | 
				
			||||||
    "astro-breadcrumbs": "^2.3.1",
 | 
					    "astro-breadcrumbs": "^2.3.1",
 | 
				
			||||||
 | 
					    "hast": "^1.0.0",
 | 
				
			||||||
    "hast-util-from-html": "^2.0.3",
 | 
					    "hast-util-from-html": "^2.0.3",
 | 
				
			||||||
    "node": "npm:22.7.0",
 | 
					    "node": "npm:22.7.0",
 | 
				
			||||||
    "remark-github-blockquote-alert": "^1.2.1",
 | 
					    "remark-github-blockquote-alert": "^1.2.1",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,9 @@ export function groupRoutes(routes: RouteData[]): GroupedRoutes {
 | 
				
			||||||
  }, defaultValue);
 | 
					  }, defaultValue);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function processQsMarkdown(markdown: string): Promise<string> {
 | 
					export async function processQsMarkdown(
 | 
				
			||||||
 | 
					  markdown: string
 | 
				
			||||||
 | 
					): Promise<string> {
 | 
				
			||||||
  return await markdownUtils.processMarkdown(markdown);
 | 
					  return await markdownUtils.processMarkdown(markdown);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,78 +1,102 @@
 | 
				
			||||||
import type { Transformer } from "unified";
 | 
					import type { Parent, Node } from "unist";
 | 
				
			||||||
import type { Node, Parent } from "unist";
 | 
					 | 
				
			||||||
import { visit, CONTINUE, SKIP } from "unist-util-visit";
 | 
					import { visit, CONTINUE, SKIP } from "unist-util-visit";
 | 
				
			||||||
import { fromHtml } from "hast-util-from-html";
 | 
					import { fromHtml } from "hast-util-from-html";
 | 
				
			||||||
 | 
					import type { Root, Element } from "hast";
 | 
				
			||||||
import type { AstroMarkdownOptions, MarkdownProcessor } from "@astrojs/markdown-remark";
 | 
					import type {
 | 
				
			||||||
 | 
					  AstroMarkdownOptions,
 | 
				
			||||||
 | 
					  MarkdownProcessor,
 | 
				
			||||||
 | 
					  RehypePlugin,
 | 
				
			||||||
 | 
					} from "@astrojs/markdown-remark";
 | 
				
			||||||
import { createMarkdownProcessor } from "@astrojs/markdown-remark";
 | 
					import { createMarkdownProcessor } from "@astrojs/markdown-remark";
 | 
				
			||||||
 | 
					 | 
				
			||||||
import { remarkAlert } from "remark-github-blockquote-alert";
 | 
					import { remarkAlert } from "remark-github-blockquote-alert";
 | 
				
			||||||
import sectionize from "@hbsnow/rehype-sectionize";
 | 
					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
 | 
					// couldn't find the actual type to use
 | 
				
			||||||
type HtmlNode = Node & {
 | 
					interface HtmlNode extends Node {
 | 
				
			||||||
  value: string,
 | 
					  value: string;
 | 
				
			||||||
  type: string,
 | 
					  type: string;
 | 
				
			||||||
  tagName: string,
 | 
					  tagName: string;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function rehypeRewriteTypelinks(): Transformer {
 | 
					const rehypeRewriteTypelinks: RehypePlugin<[]> = () => {
 | 
				
			||||||
  return function (root: Node): Node {
 | 
					  return (root: Node): Root => {
 | 
				
			||||||
    // @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(
 | 
				
			||||||
    visit(root, (node: HtmlNode, index: number, parent: Parent) => {
 | 
					      root,
 | 
				
			||||||
      if (node.type == "element" && node.tagName == "pre") return SKIP;
 | 
					      "text",
 | 
				
			||||||
 | 
					      (node: HtmlNode, index: number, parent: Parent) => {
 | 
				
			||||||
      if (node.type == "text") {
 | 
					 | 
				
			||||||
        let changed = false;
 | 
					        let changed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        node.value = node.value.replace(/TYPE99(\w+.)99TYPE/g, (_full: string, match: string) => {
 | 
					        node.value = node.value.replace(
 | 
				
			||||||
          changed = true;
 | 
					          /TYPE99(\w+.)99TYPE/g,
 | 
				
			||||||
 | 
					          (_full: string, match: string) => {
 | 
				
			||||||
 | 
					            changed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          const linkObject = getQMLTypeLinkObject(match);
 | 
					            const linkObject = getQMLTypeLinkObject(match);
 | 
				
			||||||
          const link = getQMLTypeLink(linkObject);
 | 
					            const link = getQMLTypeLink(linkObject);
 | 
				
			||||||
          const icon = (linkObject.mtype && linkObject.mtype != "func") ? getIconForLink(linkObject.mtype, false) : null;
 | 
					            const icon =
 | 
				
			||||||
          const hasParens = linkObject.mtype == "func" || linkObject.mtype == "signal";
 | 
					              linkObject.mtype && linkObject.mtype !== "func"
 | 
				
			||||||
          const hasDot = linkObject.name && linkObject.mname;
 | 
					                ? 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>`;
 | 
					            return `<a class="type${linkObject.mtype}-link typedata-link" href=${link}>${icon ?? ""}${linkObject.name ?? ""}${hasDot ? "." : ""}${linkObject.mname ?? ""}${hasParens ? "()" : ""}</a>`;
 | 
				
			||||||
        });
 | 
					          }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (changed) {
 | 
					        if (changed) {
 | 
				
			||||||
          const fragment = fromHtml(node.value, { fragment: true });
 | 
					          const fragment = fromHtml(node.value, {
 | 
				
			||||||
 | 
					            fragment: true,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
          parent.children.splice(index, 1, ...fragment.children);
 | 
					          parent.children.splice(index, 1, ...fragment.children);
 | 
				
			||||||
          return SKIP;
 | 
					          return SKIP;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CONTINUE;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return root as Root;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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>`;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return CONTINUE;
 | 
					      if (hasQSLink) {
 | 
				
			||||||
    });
 | 
					        code.replace(qsRegExp, (_full: string, match: string) => {
 | 
				
			||||||
 | 
					          const linkObject = getQMLTypeLinkObject(match);
 | 
				
			||||||
    return root;
 | 
					          const link = getQMLTypeLink(linkObject);
 | 
				
			||||||
  }
 | 
					          return `<a href=${link}>${linkObject.name ?? ""}</a>`;
 | 
				
			||||||
}
 | 
					        });
 | 
				
			||||||
 | 
					 | 
				
			||||||
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 = {
 | 
					export const markdownConfig: AstroMarkdownOptions = {
 | 
				
			||||||
| 
						 | 
					@ -80,16 +104,14 @@ export const markdownConfig: AstroMarkdownOptions = {
 | 
				
			||||||
  shikiConfig: {
 | 
					  shikiConfig: {
 | 
				
			||||||
    theme: "material-theme-ocean",
 | 
					    theme: "material-theme-ocean",
 | 
				
			||||||
    wrap: true,
 | 
					    wrap: true,
 | 
				
			||||||
    transformers: [ shikiRewriteTypelinks ],
 | 
					    transformers: [shikiRewriteTypelinks()],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  remarkPlugins: [[
 | 
					  remarkPlugins: [[remarkAlert, { legacyTitle: true }]],
 | 
				
			||||||
    remarkAlert,
 | 
					 | 
				
			||||||
    { legacyTitle: true },
 | 
					 | 
				
			||||||
  ]],
 | 
					 | 
				
			||||||
  rehypePlugins: [
 | 
					  rehypePlugins: [
 | 
				
			||||||
    [ sectionize, { idPropertyName: "id" } ],
 | 
					    // FIXME: incompatible types between unified/Plugin and Astro/RehypePlugin
 | 
				
			||||||
    // @ts-ignore: can't tell what this wants
 | 
					    // @ts-expect-error
 | 
				
			||||||
    [ rehypeRewriteTypelinks, {} ],
 | 
					    [sectionize as RehypePlugin, { idPropertyName: "id" }],
 | 
				
			||||||
 | 
					    rehypeRewriteTypelinks,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,12 +119,16 @@ let globalMarkdownProcessor: Promise<MarkdownProcessor>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function getMarkdownProcessor(): Promise<MarkdownProcessor> {
 | 
					async function getMarkdownProcessor(): Promise<MarkdownProcessor> {
 | 
				
			||||||
  if (!globalMarkdownProcessor) {
 | 
					  if (!globalMarkdownProcessor) {
 | 
				
			||||||
    globalMarkdownProcessor = createMarkdownProcessor(markdownConfig);
 | 
					    globalMarkdownProcessor =
 | 
				
			||||||
 | 
					      createMarkdownProcessor(markdownConfig);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return globalMarkdownProcessor;
 | 
					  return globalMarkdownProcessor;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function processMarkdown(markdown: string): Promise<string> {
 | 
					export async function processMarkdown(
 | 
				
			||||||
  return (await (await getMarkdownProcessor()).render(markdown)).code;
 | 
					  markdown: string
 | 
				
			||||||
 | 
					): Promise<string> {
 | 
				
			||||||
 | 
					  return (await (await getMarkdownProcessor()).render(markdown))
 | 
				
			||||||
 | 
					    .code;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4364,6 +4364,13 @@ __metadata:
 | 
				
			||||||
  languageName: node
 | 
					  languageName: node
 | 
				
			||||||
  linkType: hard
 | 
					  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":
 | 
					"hastscript@npm:^7.0.0":
 | 
				
			||||||
  version: 7.2.0
 | 
					  version: 7.2.0
 | 
				
			||||||
  resolution: "hastscript@npm:7.2.0"
 | 
					  resolution: "hastscript@npm:7.2.0"
 | 
				
			||||||
| 
						 | 
					@ -6312,6 +6319,7 @@ __metadata:
 | 
				
			||||||
    "@types/node": "npm:^20.14.11"
 | 
					    "@types/node": "npm:^20.14.11"
 | 
				
			||||||
    astro: "npm:^4.15.9"
 | 
					    astro: "npm:^4.15.9"
 | 
				
			||||||
    astro-breadcrumbs: "npm:^2.3.1"
 | 
					    astro-breadcrumbs: "npm:^2.3.1"
 | 
				
			||||||
 | 
					    hast: "npm:^1.0.0"
 | 
				
			||||||
    hast-util-from-html: "npm:^2.0.3"
 | 
					    hast-util-from-html: "npm:^2.0.3"
 | 
				
			||||||
    node: "npm:22.7.0"
 | 
					    node: "npm:22.7.0"
 | 
				
			||||||
    pagefind: "npm:^1.1.1"
 | 
					    pagefind: "npm:^1.1.1"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue