-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(docs): warning for dynamic code blocks (#989)
This PR introduces a dynamic warning to code snippets in docs that will warn whenever code snippets do not match with the same code in `main`. This also introduces the restriction that code snippets may only be created with URLs containing a commit hash. To test this feature, you can add this code to any markdown file (such as for example in a new file `./docs/site/docs/test.md`) and visit it: ```markdown # import GitHubCodeBlock from '@site/src/components/GitHubCodeBlock/GitHubCodeBlock'; ## Out of Date Code <GitHubCodeBlock repoUrl="https://github.com/omni-network/omni/blob/97df20bfb1411f30b01d79411cb8b3d0f9213573/contracts/src/pkg/XApp.sol#L60-L69" /> ## Code Line Ref Without Commit Hash <GitHubCodeBlock repoUrl="https://github.com/omni-network/omni/blob/v0.1.0/contracts/src/pkg/XApp.sol#L60-L69" /> ``` task: https://app.asana.com/0/1206208509925075/1207138429056637/f --------- Co-authored-by: Kevin Halliday <kevin@omni.network>
- Loading branch information
1 parent
d691e4b
commit b21a4ee
Showing
16 changed files
with
275 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Client module entrypoint | ||
// All modules loaded globally on site | ||
// See https://docusaurus.io/docs/api/docusaurus-config#clientModules | ||
|
||
import "./react-query-client" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { QueryClient } from '@tanstack/react-query' | ||
|
||
export const client = new QueryClient() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 67 additions & 78 deletions
145
docs/site/src/components/GitHubCodeBlock/GitHubCodeBlock.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,82 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import axios from 'axios'; | ||
import CodeBlock from "@theme/CodeBlock"; | ||
import { useColorMode } from '@docusaurus/theme-common'; | ||
import { useColorMode } from '@docusaurus/theme-common' | ||
import CodeBlock from '@theme/CodeBlock' | ||
import { getNumberOfLines, useCodeBlock } from './useCodeBlock' | ||
|
||
import './GitHubCodeBlock.css'; | ||
import './GitHubCodeBlock.css' | ||
|
||
const GitHubCodeBlock = ({ repoUrl }) => { | ||
const [code, setCode] = useState(''); | ||
const [language, setLanguage] = useState('plaintext'); | ||
const [sourceUrl, setSourceUrl] = useState(''); | ||
const GitHubCodeBlock = ({ url }: { url: string }) => { | ||
const { data, error, isLoading, isError } = useCodeBlock({ url }) | ||
|
||
useEffect(() => { | ||
async function fetchCode() { | ||
// Use a single regex that can optionally capture line numbers | ||
const regex = /(https:\/\/github\.com\/)([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/([^#]+)#L(\d+)-L(\d+)/; | ||
var match = repoUrl.match(regex); | ||
if (!match) { | ||
const noLinesRegex = /(https:\/\/github\.com\/)([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/([^#]+)/; | ||
match = repoUrl.match(noLinesRegex); | ||
} | ||
if (isLoading) return <CodeBlockLoading numLines={getNumberOfLines(url)}/> | ||
if (isError) return <CodeBlockError url={url} error={error} /> | ||
|
||
if (match) { | ||
const [wholeUrl, site, owner, repo, branch, filePath, startLine, endLine] = match; | ||
// Build the source URL to view on GitHub, including line numbers if available | ||
setSourceUrl(`https://github.com/${owner}/${repo}/blob/${branch}/${filePath}${startLine ? `#L${startLine}-L${endLine}` : ''}`); | ||
// Fetch the raw content from GitHub | ||
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}` + (startLine && endLine ? `#L${startLine}-L${endLine}` : ''); | ||
const { code, matchesMain, mainURL, sourceURL, language } = data | ||
|
||
try { | ||
const response = await axios.get(rawUrl); | ||
const allLines = response.data.split('\n'); | ||
// Apply line slicing only if line numbers are provided and valid | ||
const lines = (startLine && endLine) ? allLines.slice(parseInt(startLine) - 1, parseInt(endLine)).join('\n') : allLines.join('\n'); | ||
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"}`); | ||
} | ||
} else { | ||
console.error("Regex match failed. Check the URL format."); | ||
setCode("Error: Invalid GitHub URL format."); | ||
} | ||
} | ||
return ( | ||
<div className="code-snippet-container"> | ||
<CodeBlock language={language} className="code-snippet-block"> | ||
{code} | ||
</CodeBlock> | ||
|
||
{!matchesMain && ( | ||
<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 </a> | ||
<GithubIcon /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
fetchCode(); | ||
}, [repoUrl]); | ||
const GithubIcon = () => { | ||
const { colorMode } = useColorMode() | ||
return ( | ||
<img | ||
src={ | ||
colorMode === 'dark' | ||
? '/img/github-icon-light.svg' | ||
: '/img/github-icon-dark.svg' | ||
} | ||
alt="GitHub" | ||
/> | ||
) | ||
} | ||
|
||
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'; | ||
} | ||
const CodeBlockLoading = ({ numLines }: { numLines: number }) => { | ||
const blankLines = Array.from({ length: numLines }, (_, index) => ( | ||
<div key={index} className="loading-line" /> | ||
)); | ||
|
||
function getGitHubIcon() { | ||
const { colorMode } = useColorMode(); | ||
return colorMode === 'dark' ? "/img/github-icon-light.svg" : "/img/github-icon-dark.svg"; | ||
} | ||
return ( | ||
<div className="code-snippet-container"> | ||
<CodeBlock language="plaintext" className="code-snippet-block"> | ||
Fetching code from GitHub... | ||
{blankLines} | ||
</CodeBlock> | ||
</div> | ||
); | ||
}; | ||
|
||
const CodeBlockError = ({ url, error }: { url: string; error: Error }) => { | ||
console.error('CodeBlockError:', error) | ||
return ( | ||
<div className="code-snippet-container"> | ||
<CodeBlock language={language} className="code-snippet-block">{code}</CodeBlock> | ||
<CodeBlock language="plaintext" className="code-snippet-block"> | ||
Oops :( Something went wrong. | ||
</CodeBlock> | ||
<div className="code-snippet-footer"> | ||
<a href={sourceUrl} target="_blank" rel="noopener noreferrer"> | ||
See source on GitHub <img src={getGitHubIcon()} alt="GitHub" /> | ||
</a> | ||
<a href={url}>Check source on GitHub </a> | ||
<GithubIcon /> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
) | ||
} | ||
|
||
export default GitHubCodeBlock; | ||
export default GitHubCodeBlock |
Oops, something went wrong.