chore: refactored natspec and parsing
08dc095e
4 file(s) · +93 −125
| 3 | 3 | import { Marked } from 'marked'; |
|
| 4 | 4 | import { markedUiExtension } from '@markdown-ui/marked-ext'; |
|
| 5 | 5 | import '@markdown-ui/react/widgets.css'; |
|
| 6 | + | import { parseContractToMarkdown, type ContractResponse } from './utils/contractParser'; |
|
| 6 | 7 | ||
| 7 | 8 | const marked = new Marked().use(markedUiExtension); |
|
| 8 | 9 | ||
| 9 | - | const CONTRACT_ADDRESS = "0xF7eb390231F0Db11C673390f3C25e613D7228659"; |
|
| 10 | + | const CONTRACT_ADDRESS = "0xEeF9B4a84C3327860CD14E1E066D7D6762b9bC3F"; |
|
| 10 | 11 | const CHAIN_ID = "11155111"; // Sepolia |
|
| 11 | 12 | ||
| 12 | - | interface DevDocMethod { |
|
| 13 | - | details?: string; |
|
| 14 | - | params?: Record<string, string>; |
|
| 15 | - | } |
|
| 16 | - | ||
| 17 | - | interface DevDoc { |
|
| 18 | - | kind?: string; |
|
| 19 | - | title?: string; |
|
| 20 | - | details?: string; |
|
| 21 | - | methods: Record<string, DevDocMethod>; |
|
| 22 | - | stateVariables?: Record<string, { details: string }>; |
|
| 23 | - | version?: number; |
|
| 24 | - | } |
|
| 25 | - | ||
| 26 | - | interface ContractResponse { |
|
| 27 | - | devdoc: DevDoc; |
|
| 28 | - | matchId?: string; |
|
| 29 | - | creationMatch?: string; |
|
| 30 | - | runtimeMatch?: string; |
|
| 31 | - | verifiedAt?: string; |
|
| 32 | - | match?: string; |
|
| 33 | - | chainId?: string; |
|
| 34 | - | address?: string; |
|
| 35 | - | } |
|
| 36 | - | ||
| 37 | 13 | function App() { |
|
| 38 | 14 | const [contractHtml, setContractHtml] = useState<string>(""); |
|
| 39 | 15 | const [loading, setLoading] = useState<boolean>(true); |
|
| 53 | 29 | ||
| 54 | 30 | const data: ContractResponse = await response.json(); |
|
| 55 | 31 | ||
| 56 | - | let markdownContent = ""; |
|
| 57 | - | ||
| 58 | - | // Add contract header information |
|
| 59 | - | if (data.devdoc?.title) { |
|
| 60 | - | markdownContent += `# ${data.devdoc.title}\n\n`; |
|
| 61 | - | } |
|
| 62 | - | ||
| 63 | - | if (data.devdoc?.details) { |
|
| 64 | - | markdownContent += `${data.devdoc.details}\n\n`; |
|
| 65 | - | } |
|
| 66 | - | ||
| 67 | - | // Add contract verification info |
|
| 68 | - | if (data.address && data.chainId) { |
|
| 69 | - | markdownContent += `**Contract:** \`${data.address}\` on Chain ID \`${data.chainId}\`\n\n`; |
|
| 70 | - | } |
|
| 71 | - | ||
| 72 | - | if (data.verifiedAt) { |
|
| 73 | - | markdownContent += `**Verified:** ${new Date(data.verifiedAt).toLocaleDateString()}\n\n`; |
|
| 74 | - | } |
|
| 75 | - | ||
| 76 | - | // Process methods |
|
| 77 | - | if (data.devdoc?.methods) { |
|
| 78 | - | Object.entries(data.devdoc.methods).forEach(([methodName, method]) => { |
|
| 79 | - | markdownContent += `## ${methodName}\n\n`; |
|
| 80 | - | ||
| 81 | - | // Add method description (without markdown widgets) |
|
| 82 | - | if (method.details) { |
|
| 83 | - | const detailsWithoutWidgets = method.details.replace(/```markdown-ui-widget[\s\S]*?```/g, '').trim(); |
|
| 84 | - | if (detailsWithoutWidgets) { |
|
| 85 | - | markdownContent += `${detailsWithoutWidgets}\n\n`; |
|
| 86 | - | } |
|
| 87 | - | ||
| 88 | - | // Extract and format markdown widgets |
|
| 89 | - | const widgetMatches = method.details.match(/```markdown-ui-widget[\s\S]*?```/g); |
|
| 90 | - | if (widgetMatches) { |
|
| 91 | - | widgetMatches.forEach(widget => { |
|
| 92 | - | // Extract the content between the backticks |
|
| 93 | - | const widgetContent = widget.replace(/```markdown-ui-widget\s*/, '').replace(/```$/, '').trim(); |
|
| 94 | - | ||
| 95 | - | // Try to parse as JSON to validate format |
|
| 96 | - | try { |
|
| 97 | - | JSON.parse(widgetContent); |
|
| 98 | - | // If valid JSON, format it properly |
|
| 99 | - | markdownContent += `\`\`\`markdown-ui-widget\n${widgetContent}\n\`\`\`\n\n`; |
|
| 100 | - | } catch (e) { |
|
| 101 | - | // If not valid JSON, treat as DSL or skip |
|
| 102 | - | console.warn('Invalid JSON in widget:', widgetContent); |
|
| 103 | - | markdownContent += `\`\`\`markdown-ui-widget\n${widgetContent}\n\`\`\`\n\n`; |
|
| 104 | - | } |
|
| 105 | - | }); |
|
| 106 | - | } |
|
| 107 | - | } |
|
| 108 | - | ||
| 109 | - | // Add parameter documentation |
|
| 110 | - | if (method.params) { |
|
| 111 | - | Object.entries(method.params).forEach(([paramName, paramDesc]) => { |
|
| 112 | - | markdownContent += `**${paramName}:** `; |
|
| 113 | - | ||
| 114 | - | // Add param description (without markdown widgets) |
|
| 115 | - | const paramWithoutWidgets = paramDesc.replace(/```markdown-ui-widget[\s\S]*?```/g, '').trim(); |
|
| 116 | - | if (paramWithoutWidgets) { |
|
| 117 | - | markdownContent += `${paramWithoutWidgets}\n\n`; |
|
| 118 | - | } |
|
| 119 | - | ||
| 120 | - | // Extract and format parameter widgets |
|
| 121 | - | const paramWidgetMatches = paramDesc.match(/```markdown-ui-widget[\s\S]*?```/g); |
|
| 122 | - | if (paramWidgetMatches) { |
|
| 123 | - | paramWidgetMatches.forEach(widget => { |
|
| 124 | - | const widgetContent = widget.replace(/```markdown-ui-widget\s*/, '').replace(/```$/, '').trim(); |
|
| 125 | - | ||
| 126 | - | // Try to parse as JSON to validate format |
|
| 127 | - | try { |
|
| 128 | - | JSON.parse(widgetContent); |
|
| 129 | - | // If valid JSON, format it properly |
|
| 130 | - | markdownContent += `\`\`\`markdown-ui-widget\n${widgetContent}\n\`\`\`\n\n`; |
|
| 131 | - | } catch (e) { |
|
| 132 | - | // If not valid JSON, treat as DSL or skip |
|
| 133 | - | console.warn('Invalid JSON in param widget:', widgetContent); |
|
| 134 | - | markdownContent += `\`\`\`markdown-ui-widget\n${widgetContent}\n\`\`\`\n\n`; |
|
| 135 | - | } |
|
| 136 | - | }); |
|
| 137 | - | } |
|
| 138 | - | }); |
|
| 139 | - | } |
|
| 140 | - | ||
| 141 | - | markdownContent += "\n"; |
|
| 142 | - | }); |
|
| 143 | - | } |
|
| 32 | + | const markdownContent = parseContractToMarkdown(data); |
|
| 144 | 33 | ||
| 145 | 34 | console.log(markdownContent); |
|
| 146 | 35 | const html = await marked.parse(markdownContent || '# No markdown widgets found'); |
|
| 1 | - | @import "tailwindcss"; |
|
| 1 | + | html { |
|
| 2 | + | font-family: sans-serif; |
|
| 3 | + | padding: 2rem 4rem; |
|
| 4 | + | max-width: 700px; |
|
| 5 | + | margin: auto; |
|
| 6 | + | } |
| 1 | + | interface DevDocMethod { |
|
| 2 | + | details?: string; |
|
| 3 | + | params?: Record<string, string>; |
|
| 4 | + | } |
|
| 5 | + | ||
| 6 | + | interface DevDoc { |
|
| 7 | + | kind?: string; |
|
| 8 | + | title?: string; |
|
| 9 | + | details?: string; |
|
| 10 | + | methods: Record<string, DevDocMethod>; |
|
| 11 | + | stateVariables?: Record<string, { details: string }>; |
|
| 12 | + | version?: number; |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | interface ContractResponse { |
|
| 16 | + | devdoc: DevDoc; |
|
| 17 | + | matchId?: string; |
|
| 18 | + | creationMatch?: string; |
|
| 19 | + | runtimeMatch?: string; |
|
| 20 | + | verifiedAt?: string; |
|
| 21 | + | match?: string; |
|
| 22 | + | chainId?: string; |
|
| 23 | + | address?: string; |
|
| 24 | + | } |
|
| 25 | + | ||
| 26 | + | export function parseContractToMarkdown(data: ContractResponse): string { |
|
| 27 | + | let markdownContent = ""; |
|
| 28 | + | ||
| 29 | + | // Add contract header information |
|
| 30 | + | if (data.devdoc?.title) { |
|
| 31 | + | markdownContent += `# ${data.devdoc.title}\n\n`; |
|
| 32 | + | } |
|
| 33 | + | ||
| 34 | + | if (data.devdoc?.details) { |
|
| 35 | + | markdownContent += `${data.devdoc.details}\n\n`; |
|
| 36 | + | } |
|
| 37 | + | ||
| 38 | + | // Add contract verification info |
|
| 39 | + | if (data.address && data.chainId) { |
|
| 40 | + | markdownContent += `**Contract:** \`${data.address}\` on Chain ID \`${data.chainId}\`\n\n`; |
|
| 41 | + | } |
|
| 42 | + | ||
| 43 | + | if (data.verifiedAt) { |
|
| 44 | + | markdownContent += `**Verified:** ${new Date(data.verifiedAt).toLocaleDateString()}\n\n`; |
|
| 45 | + | } |
|
| 46 | + | ||
| 47 | + | // Process methods |
|
| 48 | + | if (data.devdoc?.methods) { |
|
| 49 | + | Object.entries(data.devdoc.methods).forEach(([methodName, method]) => { |
|
| 50 | + | markdownContent += `## ${methodName}\n\n`; |
|
| 51 | + | ||
| 52 | + | if (method.details) { |
|
| 53 | + | // Replace \n with actual newlines |
|
| 54 | + | const processedDetails = method.details.replace(/\\n/g, '\n'); |
|
| 55 | + | markdownContent += `${processedDetails}\n\n`; |
|
| 56 | + | } |
|
| 57 | + | ||
| 58 | + | if (method.params) { |
|
| 59 | + | Object.entries(method.params).forEach(([paramName, paramDesc]) => { |
|
| 60 | + | markdownContent += `**${paramName}:** `; |
|
| 61 | + | // Replace \n with actual newlines |
|
| 62 | + | const processedParamDesc = paramDesc.replace(/\\n/g, '\n'); |
|
| 63 | + | markdownContent += `${processedParamDesc}\n\n`; |
|
| 64 | + | }); |
|
| 65 | + | } |
|
| 66 | + | ||
| 67 | + | markdownContent += "\n"; |
|
| 68 | + | }); |
|
| 69 | + | } |
|
| 70 | + | ||
| 71 | + | return markdownContent; |
|
| 72 | + | } |
|
| 73 | + | ||
| 74 | + | export type { ContractResponse, DevDoc, DevDocMethod }; |
| 11 | 11 | ||
| 12 | 12 | /// @notice Sets the counter to a specific value |
|
| 13 | 13 | /// @dev Updates the number state variable to the provided value |
|
| 14 | - | /// @param newNumber The new value to set the counter to |
|
| 15 | - | /// |
|
| 16 | - | /// ```markdown-ui-widget |
|
| 17 | - | /// { "type": "form", "id": "setNumber", "submitLabel": "Set Number", "fields": [{ "type": "text-input", "id": "newValue", "label": "New Counter Value", "placeholder": "Enter number", "default": "42" }] } |
|
| 18 | - | /// ``` |
|
| 14 | + | /// @param newNumber The new value to set the counter to \n |
|
| 15 | + | /// \n |
|
| 16 | + | /// ```markdown-ui-widget \n |
|
| 17 | + | /// { "type": "form", "id": "setNumber", "submitLabel": "Set Number", "fields": [{ "type": "text-input", "id": "newValue", "label": "New Counter Value", "placeholder": "Enter number", "default": "42" }] } \n |
|
| 18 | + | /// ``` \n |
|
| 19 | 19 | function setNumber(uint256 newNumber) public { |
|
| 20 | 20 | number = newNumber; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// @notice Increments the counter by 1 |
|
| 24 | - | /// @dev Increases the number state variable by 1 using the increment operator |
|
| 25 | - | /// |
|
| 26 | - | /// ```markdown-ui-widget |
|
| 27 | - | /// { "type": "form", "id": "increment", "submitLabel": "Increment", "fields": [] } |
|
| 28 | - | /// ``` |
|
| 24 | + | /// @dev Increases the number state variable by 1 using the increment operator \n |
|
| 25 | + | /// \n |
|
| 26 | + | /// ```markdown-ui-widget \n |
|
| 27 | + | /// { "type": "form", "id": "increment", "submitLabel": "Increment", "fields": [] } \n |
|
| 28 | + | /// ```\n |
|
| 29 | 29 | function increment() public { |
|
| 30 | 30 | number++; |
|
| 31 | 31 | } |