Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
taga3s committed Dec 16, 2024
1 parent c6b099f commit bbe0f25
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 55 deletions.
77 changes: 57 additions & 20 deletions app/packages/rehype-github-embed/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { Element, Root } from "hast";
import type { Plugin, Transformer } from "unified";
import { visit } from "unist-util-visit";
import { type Lines, buildRequestURL, createCodeBlock, extractCodeByLines, extractRepoDataFromURL } from "./utils";
import {
type Lines,
buildRequestURL,
createCodeBlock,
createErrBlock,
extractCodeByLines,
extractRepoDataFromURL,
} from "./utils";

type Option = {
githubPAT: string;
Expand All @@ -11,10 +18,6 @@ const rehypeGithubEmbed: Plugin<Option[], Root> = (options): Transformer<Root> =
const { githubPAT } = options;

const transform: Transformer<Root> = async (tree) => {
const regex = new RegExp(
/https:\/\/github\.com\/[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+\/blob\/[a-zA-Z0-9._-]+\/?[^\s]*/g,
);

const githubEmbedPromises: Promise<void>[] = [];

const headers = {
Expand All @@ -24,11 +27,15 @@ const rehypeGithubEmbed: Plugin<Option[], Root> = (options): Transformer<Root> =
};

visit(tree, "element", (node, index, parent) => {
const regex = new RegExp(
/https:\/\/github\.com\/[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+\/blob\/[a-zA-Z0-9._-]+\/?[^\s]*/g,
);

if (
node.tagName !== "a" ||
node.properties.href === undefined ||
typeof node.properties.href !== "string" ||
!regex.test(node.properties.href)
node.children.length === 0 ||
node.children[0].type !== "text" ||
!regex.test(node.children[0].value)
) {
return;
}
Expand All @@ -37,39 +44,69 @@ const rehypeGithubEmbed: Plugin<Option[], Root> = (options): Transformer<Root> =
return;
}

if (parent.type === "mdxJsxFlowElement" || parent.type === "mdxJsxTextElement") {
return;
}

type Props = {
node: Element;
parent: Root | Element;
href: string;
owner: string;
repoName: string;
ref: string;
path: string;
lines?: Lines;
};

const githubEmbedPromise = async ({ repoName, ref, href, path, lines }: Props): Promise<void> => {
const requestUrl = buildRequestURL(repoName, ref, path);
const githubEmbedPromise = async ({ parent, owner, repoName, ref, href, path, lines }: Props): Promise<void> => {
const requestUrl = buildRequestURL(owner, repoName, ref, path);

try {
const response = await fetch(requestUrl, { headers });
const response = await fetch(requestUrl, {
headers,
});
if (!response.ok) {
if (response.status === 404) {
const element = createErrBlock("404 Not Found");
parent.children[index] = element;
return;
}

throw new Error(`Failed to fetch code from GitHub: ${response.status}`);
}
const content = (await response.json()) as { content: string };
const content = (await response.json()) as {
content: string;
};
const codeBase64 = content.content;
const code = extractCodeByLines(Buffer.from(codeBase64, "base64").toString("utf-8"), lines);

const title = `${repoName}/${path}${lines ? `#L${lines.startLine}-L${lines.endLine}` : ""}`;
const codeBlockElement = createCodeBlock(title, href, code);
const title = `${repoName}/${path}${
lines
? `#${lines.startLine && lines.endLine ? `L${lines.startLine}-L${lines.endLine}` : `L${lines.startLine}`}`
: ""
}`;

parent.children[index] = codeBlockElement;
const element = createCodeBlock(title, href, code);
parent.children[index] = element;
} catch (error) {
throw new Error("Failed to fetch code from GitHub", { cause: error });
}
};

const nodeHref = node.properties.href;
const { repoName, ref, path, lines } = extractRepoDataFromURL(nodeHref);

githubEmbedPromises.push(githubEmbedPromise({ node, repoName, href: nodeHref, ref, path, lines }));
const nodeHref = node.children[0].value;
const { repoName, owner, ref, path, lines } = extractRepoDataFromURL(nodeHref);

githubEmbedPromises.push(
githubEmbedPromise({
parent,
repoName,
owner,
href: nodeHref,
ref,
path,
lines,
}),
);
});

await Promise.all(githubEmbedPromises);
Expand Down
79 changes: 44 additions & 35 deletions app/packages/rehype-github-embed/utils.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,66 @@
import { h } from "hastscript";

const extractRepoName = (urlParts: string[]): string => {
return urlParts.slice(0, 2).join("/");
const extractOwner = (pathname: string): string => {
const pathnameParts = pathname.split("/");
return pathnameParts[1];
};

const extractFilePath = (urlParts: string[]): string => {
const result = urlParts.slice(4).join("/");
const hashIndex = result.indexOf("#");
if (hashIndex === -1) {
return result;
}
return result.slice(0, hashIndex);
const extractRepoName = (pathname: string): string => {
const pathnameParts = pathname.split("/");
return pathnameParts[2];
};

const extractFilePath = (pathname: string): string => {
const pathnameParts = pathname.split("/");
return pathnameParts.slice(5).join("/");
};

const extractRef = (urlParts: string[]): string => {
return urlParts[3];
const extractRef = (pathname: string): string => {
const pathnameParts = pathname.split("/");
return pathnameParts[4];
};

export type Lines = {
startLine: number;
endLine: number;
endLine?: number;
};

const extractLines = (urlParts: string[]): Lines | undefined => {
const result = urlParts.slice(4).join("/");
const hashIndex = result.indexOf("#");
if (hashIndex === -1) {
return;
}
const hash = result.slice(hashIndex + 1);
const numbers = hash.match(/\d+/g);
if (!numbers) return;
return {
startLine: Number.parseInt(numbers[0]),
endLine: Number.parseInt(numbers[1]),
};
const extractLines = (hash: string): Lines | undefined => {
const matches = hash.match(/\d+/g);
if (!matches) return;

return matches[0] && matches[1]
? {
startLine: Number.parseInt(matches[0]),
endLine: Number.parseInt(matches[1]),
}
: { startLine: Number.parseInt(matches[0]) };
};

const extractRepoDataFromURL = (url: string) => {
const base = url.replace("https://github.com/", "");
const urlParts = base.split("/");
const base = new URL(url);
return {
repoName: extractRepoName(urlParts),
ref: extractRef(urlParts),
path: extractFilePath(urlParts),
lines: extractLines(urlParts),
repoName: extractRepoName(base.pathname),
owner: extractOwner(base.pathname),
ref: extractRef(base.pathname),
path: extractFilePath(base.pathname),
lines: extractLines(base.hash),
};
};

const extractCodeByLines = (code: string, lines: Lines | undefined): string => {
if (!lines) return code;
const codeLines = code.split("\n");
return codeLines.slice(lines.startLine - 1, lines.endLine).join("\n");

if (lines.startLine && lines.endLine) {
return codeLines.slice(lines.startLine - 1, lines.endLine).join("\n");
}

return codeLines[lines.startLine - 1];
};

const buildRequestURL = (repo: string, ref: string, path: string): string => {
return `https://api.github.com/repos/${repo}/contents/${path}?ref=${ref}`;
const buildRequestURL = (owner: string, repoName: string, ref: string, path: string): string => {
return `https://api.github.com/repos/${owner}/${repoName}/contents/${path}?ref=${ref}`;
};

const createCodeBlock = (title: string, href: string, code: string) => {
Expand All @@ -67,4 +72,8 @@ const createCodeBlock = (title: string, href: string, code: string) => {
]);
};

export { extractRepoDataFromURL, buildRequestURL, extractCodeByLines, createCodeBlock };
const createErrBlock = (message: string) => {
return h("div", [h("span", [h(null, [{ type: "text", value: message }])])]);
};

export { extractRepoDataFromURL, buildRequestURL, extractCodeByLines, createCodeBlock, createErrBlock };

0 comments on commit bbe0f25

Please sign in to comment.