diff --git a/src/data-layer/fetchers/developer-tools/fetchBuidlGuidl.ts b/src/data-layer/fetchers/developer-tools/fetchBuidlGuidl.ts index d575e492ca0..7344adbe107 100644 --- a/src/data-layer/fetchers/developer-tools/fetchBuidlGuidl.ts +++ b/src/data-layer/fetchers/developer-tools/fetchBuidlGuidl.ts @@ -2,6 +2,35 @@ import { DeveloperToolsResponse } from "@/lib/types" import { fetchRetry } from "@/data-layer/fetchers/fetchRetry" +/** + * Normalizes a URL field by ensuring it has an https:// protocol prefix. + * Returns undefined for empty/whitespace-only values. + */ +function normalizeUrl(value: string | undefined): string | undefined { + if (!value) return undefined + const trimmed = value.trim() + if (!trimmed) return undefined + if (/^https?:\/\//i.test(trimmed)) return trimmed + return `https://${trimmed}` +} + +/** + * Normalizes a Twitter/X social field into a full URL. + * Handles bare handles (@foo, foo), partial URLs (x.com/foo, twitter.com/foo), + * and full URLs. + */ +function normalizeTwitter(value: string | undefined): string | undefined { + if (!value) return undefined + const trimmed = value.trim() + if (!trimmed) return undefined + if (/^https?:\/\//i.test(trimmed)) return trimmed + // Already a partial domain URL (x.com/..., twitter.com/...) + if (/^(x\.com|twitter\.com)\//i.test(trimmed)) return `https://${trimmed}` + // Bare handle with or without @ + const handle = trimmed.replace(/^@/, "") + return `https://x.com/${handle}` +} + export async function fetchBuidlGuidl(): Promise { const url = "https://raw.githubusercontent.com/BuidlGuidl/Developer-Tooling/refs/heads/main/output/results.json" @@ -21,5 +50,9 @@ export async function fetchBuidlGuidl(): Promise { console.log("Successfully fetched BuidlGuidl developer tooling data") - return json + return json.map((tool) => ({ + ...tool, + website: normalizeUrl(tool.website), + twitter: normalizeTwitter(tool.twitter), + })) } diff --git a/src/data-layer/mocks/fetch-developer-tools.json b/src/data-layer/mocks/fetch-developer-tools.json index b72587c1d67..8b0c37364d2 100644 --- a/src/data-layer/mocks/fetch-developer-tools.json +++ b/src/data-layer/mocks/fetch-developer-tools.json @@ -242,7 +242,7 @@ "description": "Ethers.js is a simple, compact and complete JavaScript (via TypeScript) library for interacting with Ethereum and related blockchains.\n\nIt is currently used in a very large number of Blockchain projects, including everything from block explorers to wallets (like MetaMask) and is downloaded over 7.1 million times per month (as of this writing). It is also one of the top 500 projects (by dependants) on NPM.\n\nIt was written and is maintained by me, RicMoo (Richard Moore), a random developer from Canada that is passionate about open-source and dedicates most his waking-time (and some sleeping-time) to it.\n\nHack the Planet! :)", "thumbnail_url": "https://storage.googleapis.com/op-atlas/f8d776ab-ca70-42ee-9e41-d4f709cd6fd4.png", "banner_url": "https://storage.googleapis.com/op-atlas/459bdd5e-60a5-49ca-88b8-d4537ebfec16.png", - "twitter": "@ricmoo", + "twitter": "https://x.com/ricmoo", "tags": [ "frontend", "json-rpc", @@ -628,14 +628,14 @@ "description": "Runtime Verification is a leading formal verification company specializing in blockchain security and smart contract correctness. We've developed ERCx, the most comprehensive open-source testing library for ERC token standards, featuring over 500 individual tests across ERC-20, ERC-721, ERC-1155, and ERC-4626 implementations.\nERCx directly empowers Superchain builders by providing production-ready test suites that verify both standard compliance and security properties. Our library offers zero-configuration testing for deployed contracts via Foundry fork testing, plus simple integration for pre-deployment source code validation. With three testing tiers: Standard (EIP compliance), Security (vulnerability detection), and Features (implementation validation), developers can ship token contracts with confidence, knowing they've been thoroughly vetted against real-world attack vectors and edge cases.\nWhat makes ERCx particularly valuable for the Optimism ecosystem is its cross-chain compatibility and handling of complex deployment scenarios. The library seamlessly works across OP Stack chains and handles storage complexities that often challenge developers working with established tokens like USDC or stETH. By providing this critical testing infrastructure as open-source tooling, we're enabling safer, more reliable token implementations across the entire Superchain, directly supporting the ecosystem's growth while reducing the security risks that have historically plagued token contracts in DeFi.", "thumbnail_url": "https://storage.googleapis.com/op-atlas/e6a6dd9b-4ace-4187-8408-0e5729bb0265.png", "banner_url": "https://storage.googleapis.com/op-atlas/eee0738d-3735-42c8-8802-93b22a041803.png", - "twitter": "x.com/rv_inc", + "twitter": "https://x.com/rv_inc", "tags": [ "foundry", "security", "erc721", "runtime-verification" ], - "website": "ercx.runtimeverification.com", + "website": "https://ercx.runtimeverification.com", "category": "Security, Testing & Formal Verification", "repos": [ { diff --git a/src/scripts/i18n/post_import_sanitize.ts b/src/scripts/i18n/post_import_sanitize.ts index 899a07cd8a0..dda3f195dd4 100644 --- a/src/scripts/i18n/post_import_sanitize.ts +++ b/src/scripts/i18n/post_import_sanitize.ts @@ -2891,12 +2891,10 @@ function fixSpanWrappedBackticks(content: string): { const parts = content.split(fencePattern) // Pattern 1: wrapping backtick content - const spanAroundBacktickRe = - /\s*(`[^`]+`)\s*<\/span>/g + const spanAroundBacktickRe = /\s*(`[^`]+`)\s*<\/span>/g // Pattern 2: backticks wrapping content (makes span visible as code) - const backtickAroundSpanRe = - /`\s*([\s\S]+?)\s*<\/span>`/g + const backtickAroundSpanRe = /`\s*([\s\S]+?)\s*<\/span>`/g for (let i = 0; i < parts.length; i++) { if (i % 2 === 1) continue // Skip code fences diff --git a/tests/unit/sanitizer/standalone-fixes.spec.ts b/tests/unit/sanitizer/standalone-fixes.spec.ts index bc926d51b1e..2f02359d6f3 100644 --- a/tests/unit/sanitizer/standalone-fixes.spec.ts +++ b/tests/unit/sanitizer/standalone-fixes.spec.ts @@ -2491,7 +2491,8 @@ author: Ori Pomerantz test.describe("fixCrossScriptPunctuation", () => { test("replaces \u3002 with \u06D4 for locale ur", () => { - const input = "\u06CC\u06C1 \u0627\u06CC\u06A9 \u062C\u0645\u0644\u06C1 \u06C1\u06D2\u3002" + const input = + "\u06CC\u06C1 \u0627\u06CC\u06A9 \u062C\u0645\u0644\u06C1 \u06C1\u06D2\u3002" const { content, fixCount } = fixCrossScriptPunctuation(input, "ur") expect(content).toBe( "\u06CC\u06C1 \u0627\u06CC\u06A9 \u062C\u0645\u0644\u06C1 \u06C1\u06D2\u06D4" @@ -2567,7 +2568,7 @@ author: Ori Pomerantz }) test.describe("fixSpanWrappedBackticks", () => { - test("unwraps around backtick content", () => { + test('unwraps around backtick content', () => { const input = '`APPLY(S,TX) -> S\'`' const { content, fixCount } = fixSpanWrappedBackticks(input) expect(content).toBe("`APPLY(S,TX) -> S'`") @@ -2584,7 +2585,7 @@ author: Ori Pomerantz expect(fixCount).toBe(2) }) - test("does not touch wrapping non-backtick content", () => { + test('does not touch wrapping non-backtick content', () => { const input = '2026-03-15' const { content, fixCount } = fixSpanWrappedBackticks(input) expect(content).toBe(input)