Skip to content

Commit

Permalink
feat(docs): refactor to use helper in useGitHubCodeBlock
Browse files Browse the repository at this point in the history
  • Loading branch information
idea404 committed May 7, 2024
1 parent 0cd88bb commit 376fdc2
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 252 deletions.
135 changes: 30 additions & 105 deletions docs/site/src/components/GitHubCodeBlock/GitHubCodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,131 +1,56 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import CodeBlock from "@theme/CodeBlock";
import { useColorMode } from '@docusaurus/theme-common';
import CodeBlock from "@theme/CodeBlock";
import { useEffect, useState } from 'react';
import useGitHubCodeBlock from './useGitHubCodeBlock';

import './GitHubCodeBlock.css';

const GitHubCodeBlock = ({ repoUrl }) => {
const [code, setCode] = useState('');
const [language, setLanguage] = useState('plaintext');
const [sourceUrl, setSourceUrl] = useState('');
const [mainUrl, setMainUrl] = useState('');
const [isCodeMatch, setCodeMatch] = useState(true);
const [data, setData] = useState(null);
const colorMode = useColorMode();

useEffect(() => {
fetchCode();
}, [repoUrl]);

async function fetchCode() {
if (!repoUrl) {
console.error("No URL provided.");
setCode("Error: No URL provided.");
return;
}

var match = matchUrl(repoUrl);
if (!match) {
return;
}

const [wholeUrl, site, owner, repo, branch, filePath, startLine, endLine] = match;

const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}` + (startLine && endLine ? `#L${startLine}-L${endLine}` : ''); // Fetch the raw content from GitHub
const mainRawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/main/${filePath}`; // main branch raw content URL
setSourceUrl(`https://github.com/${owner}/${repo}/blob/${branch}/${filePath}` + (startLine && endLine ? `#L${startLine}-L${endLine}` : '')); // Build the source URL to view on GitHub, including line numbers if available
setMainUrl(`https://github.com/${owner}/${repo}/blob/main/${filePath}`); // main branch source URL

try {
const response = await axios.get(rawUrl); // Fetch the raw content from the specified commit hash
const lines = extractLines(response, startLine, endLine);

const mainResponse = await axios.get(mainRawUrl); // Fetch the raw content from the main branch
const mainLines = extractLines(mainResponse, startLine, endLine);

if (lines !== mainLines) { // Check if the code matches the main branch
setCodeMatch(false);
const fetchData = async () => {
try {
const result = await useGitHubCodeBlock(repoUrl);
setData(result);
} catch (error) {
console.error(error);
}
};

setCode(lines);
setLanguage(determineLanguage(filePath));
} catch (error) {
console.error('Error fetching code:', error.response ? error.response.data : error.message);
setCode(`Error: ${error.response ? error.response.data : "Could not fetch file"}`);
}
}

function extractLines(mainResponse, startLine: string, endLine: string) {
const allLines = mainResponse.data.split('\n');
const lines = (startLine && endLine) ? allLines.slice(parseInt(startLine) - 1, parseInt(endLine)).join('\n') : allLines.join('\n');
return lines;
}

function matchUrl(url: string) {
const permalinkLinesRegex = /(https:\/\/github\.com\/)([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/([^#]+)#L(\d+)-L(\d+)/;
const repoFileRegex = /(https:\/\/github\.com\/)([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/([^#]+)/;
let match = url.match(permalinkLinesRegex);
if (match) {
if (match[4].length !== 40) {
console.error("Invalid URL: Permalink with lines URL should include a commit hash.");
setCode("Error: Invalid GitHub Permalink Lines URL format.");
return null;
}
return match;
}
match = url.match(repoFileRegex);
if (!match) {
console.error("Invalid URL: Check the URL format.");
setCode("Error: Invalid GitHub URL format.");
}
return match;
}
fetchData();
}, [repoUrl]);

function determineLanguage(filePath) {
const extension = filePath.slice(filePath.lastIndexOf('.'));
const languageMap = {
'.js': 'javascript',
'.ts': 'typescript',
'.py': 'python',
'.cpp': 'cpp',
'.c': 'c',
'.java': 'java',
'.rs': 'rust',
'.html': 'html',
'.css': 'css',
'.md': 'markdown',
'.sh': 'bash',
'.sol': 'solidity',
'.go': 'go',
'.json': 'json',
'.yml': 'yaml',
'.yaml': 'yaml',
'.toml': 'toml',
'.xml': 'xml',
'.sql': 'sql',
};
return languageMap[extension] || 'plaintext';
if (!data) {
return (
<div className="code-snippet-container">
<CodeBlock language="plaintext" className="code-snippet-block">Fetching code from GitHub...</CodeBlock>
</div>
);
}

function getGitHubIcon() {
const { colorMode } = useColorMode();
return colorMode === 'dark' ? "/img/github-icon-light.svg" : "/img/github-icon-dark.svg";
}
const { code, isCodeMatch, mainUrl, sourceUrl, language, isError } = data;

return (
<div className="code-snippet-container">
<CodeBlock language={language} className="code-snippet-block">{code}</CodeBlock>

{!isCodeMatch &&
<div className="code-snippet-warning">
<a href={mainUrl}>
<strong>Warning: </strong>Code shown does not match the main branch. Please visit the repository URL for actual code.
</a>
</div>
}
<div className="code-snippet-footer">
<a href={sourceUrl}>
See source on GitHub <img src={getGitHubIcon()} alt="GitHub" />
</a>
</div>

{!isError &&
<div className="code-snippet-footer">
<a href={sourceUrl}>
See source on GitHub <img src={colorMode.colorMode === 'dark' ? "/img/github-icon-light.svg" : "/img/github-icon-dark.svg"} alt="GitHub" />
</a>
</div>
}
</div>
);
}
Expand Down
93 changes: 93 additions & 0 deletions docs/site/src/components/GitHubCodeBlock/useGitHubCodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
export default async function useGitHubCodeBlock(repoUrl: string) {
let match = matchUrl(repoUrl);
if (match[0] === "Error") {
return {
code: match[1],
isCodeMatch: true,
mainUrl: "",
sourceUrl: "",
language: "plaintext",
isError: true,
};
}

const [, , owner, repo, branch, filePath, startLine, endLine] = match;
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}` + (startLine && endLine ? `#L${startLine}-L${endLine}` : '');
const mainRawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/main/${filePath}`;

const linesRes = await fetchCodeLinesFromUrl(rawUrl, startLine, endLine);
const mainLinesRes = await fetchCodeLinesFromUrl(mainRawUrl, startLine, endLine);

const isCodeMatch = linesRes === mainLinesRes;
const sourceUrl = `https://github.com/${owner}/${repo}/blob/${branch}/${filePath}` + (startLine && endLine ? `#L${startLine}-L${endLine}` : '');
const mainUrl = `https://github.com/${owner}/${repo}/blob/main/${filePath}`;

return {
code: linesRes,
isCodeMatch,
mainUrl,
sourceUrl,
language: determineLanguage(filePath),
isError: false,
};
}

async function fetchCodeLinesFromUrl(url: string, startLine: string, endLine: string) {
const response = await fetch(url);
const data = await response.text();
const lines = extractLines(data, startLine, endLine);
return lines;
}

function extractLines(mainResponse: string, startLine: string, endLine: string) {
const allLines = mainResponse.split('\n');
const lines = (startLine && endLine) ? allLines.slice(parseInt(startLine) - 1, parseInt(endLine)).join('\n') : allLines.join('\n');
return lines;
}

function matchUrl(url: string) {
const permalinkLinesRegex = /(https:\/\/github\.com\/)([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/([^#]+)#L(\d+)-L(\d+)/;
let match = url.match(permalinkLinesRegex);
if (match) {
if (match[4].length !== 40) {
console.error("Invalid URL: Permalink with lines URL should include a commit hash.");
let err = "Error: Invalid GitHub URL with commit hash and lines format.";
return ["Error", err];
}
return match;
}
const repoFileRegex = /(https:\/\/github\.com\/)([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/([^#]+)/;
match = url.match(repoFileRegex);
if (!match) {
console.error("Invalid URL: Check the URL format.");
let err = "Error: Invalid GitHub URL format.";
return ["Error", err];
}
return match;
}

function determineLanguage(filePath: string) {
const extension = filePath.slice(filePath.lastIndexOf('.'));
const languageMap = {
'.js': 'javascript',
'.ts': 'typescript',
'.py': 'python',
'.cpp': 'cpp',
'.c': 'c',
'.java': 'java',
'.rs': 'rust',
'.html': 'html',
'.css': 'css',
'.md': 'markdown',
'.sh': 'bash',
'.sol': 'solidity',
'.go': 'go',
'.json': 'json',
'.yml': 'yaml',
'.yaml': 'yaml',
'.toml': 'toml',
'.xml': 'xml',
'.sql': 'sql',
};
return languageMap[extension] || 'plaintext';
}
70 changes: 0 additions & 70 deletions docs/site/src/components/HomepageFeatures/index.tsx

This file was deleted.

11 changes: 0 additions & 11 deletions docs/site/src/components/HomepageFeatures/styles.module.css

This file was deleted.

23 changes: 0 additions & 23 deletions docs/site/src/pages/index.module.css

This file was deleted.

Loading

0 comments on commit 376fdc2

Please sign in to comment.