diff --git a/scripts/cip.ts b/scripts/cip.ts index 8ae06f1fb8..fad723d2b6 100644 --- a/scripts/cip.ts +++ b/scripts/cip.ts @@ -4,6 +4,10 @@ import { getStringContentAsync, getBufferContentAsync, preventH1Headline, + getDocTag, + identifyReferenceLinks, + identifyMixedReferenceLinks, + identifyNestedExtendedCIPLinks, } from "./reusable"; import { cip_repo_raw_base_url, @@ -22,6 +26,57 @@ const path_name = path.basename(__filename); // Download markdown resources const processCIPContentAsync = async (cip_name: string, content: string) => { + // Finding any reference links in the content and replacing to relative links where necessary + // to fix broken links in the CIPs + const referenceLinks = identifyReferenceLinks(content); + if(referenceLinks.length > 0) { + await Promise.all( + referenceLinks.map(async (link) => { + if(link.url.indexOf("./") == 0 || link.url.indexOf("../") == 0) { + + console.log(`Found reference links in CIP ${cip_name}:`); + console.log(`WARNING: Reference link ${link.reference} in CIP ${cip_name} is an relative link: ${link.url}.`); + + // Rewrite link to static folder + content = content.replace( + `[${link.reference}]`, + `[${link.reference}](${link.url})` + ); + } + }) + ); + } + + // Handle mixed reference links + const mixedReferenceLinks = identifyMixedReferenceLinks(content); + if (mixedReferenceLinks.length > 0) { + mixedReferenceLinks.forEach(mixedLink => { + const matchedReference = referenceLinks.find(link => link.reference === mixedLink.reference && (link.url.indexOf("./") == 0 || link.url.indexOf("../") == 0)); + if (matchedReference) { + + console.log(`Found mixed reference links in CIP ${cip_name}:`); + console.log(`WARNING: Mixed reference link [${mixedLink.text}][${mixedLink.reference}] is an relative mixed link: ${matchedReference.url}.`); + + content = content.replace(`[${mixedLink.text}][${mixedLink.reference}]`, `[${mixedLink.text}](${cip_repo_raw_base_url}${cip_name}/${matchedReference.url})`); + } + }); + } + + // Handle nested extended CIP links + const nestedExtendedCIPs = identifyNestedExtendedCIPLinks(content); + if (nestedExtendedCIPs.length > 0) { + nestedExtendedCIPs.forEach(nestedCIP => { + console.log(`Found nested extended CIP links in CIP ${cip_name}:`); + console.log(`WARNING: Nested extended CIP link ${nestedCIP} in CIP ${cip_name} is a relative link.`); + + const modifiedNestedCIP = nestedCIP.replace("(", "").replace(")", ""); + + content = content.replace(nestedCIP, `(${cip_repo_base_url}${cip_name}/${modifiedNestedCIP})`); + } + ); + } + + // Handle inline links const cip_resource = content.match(cip_regex); if (cip_resource) { await Promise.all( @@ -167,11 +222,6 @@ const stringManipulation = (content: string, cip_name: string) => { return content; }; -// Get a specific doc tag -const getDocTag = (content: string, tag_name: string) => { - return content.match(new RegExp(`(?<=${tag_name}: ).*`, "")); -}; - const main = async () => { console.log("CIP Content Downloading..."); // Use https://raw.githubusercontent.com/cardano-foundation/CIPs/master/README.md as entry point to get URLs diff --git a/scripts/cps.ts b/scripts/cps.ts index 02f07f322d..b285431f31 100644 --- a/scripts/cps.ts +++ b/scripts/cps.ts @@ -8,8 +8,10 @@ import { cip_source_repo, cip_repo_base_url, cip_readme_url, + cip_repo_raw_base_url, + cip_regex, } from "./constants"; -import { getDocTag } from "./reusable"; +import { getDocTag, identifyReferenceLinks, identifyMixedReferenceLinks } from "./reusable"; // Fetch markdown files from Github async function fetchReadmeContent(folderUrl: string, folderName: string): Promise { @@ -57,13 +59,13 @@ async function updateOrCreateReadmeFile( const creationDate = getDocTag(content, "Created"); // Sanitize title - const newContent = content.replace( + content = content.replace( /Title: .+/, `Title: "${title}"\n${sidebarLabel}${custom_edit_url}` ); // Add Content Info at the bottom of the page - const newContentWithInfo = newContent.concat( + content = content.concat( "\n" + "## CPS Information \nThis CPS was created on **" + creationDate + @@ -78,16 +80,68 @@ async function updateOrCreateReadmeFile( ")." ); - // Replace image paths - const newContentWithCorrectImagePaths = newContentWithInfo.replace( - /!\[.*?\]\(\.\/(.*?)\)/g, - `![same text](../../../static/img/cps/${folderName}/$1)` - ); + const referenceLinks = identifyReferenceLinks(content); + if(referenceLinks.length > 0) { + await Promise.all( + referenceLinks.map(async (link) => { + if(link.url.indexOf("./") == 0 || link.url.indexOf("../") == 0) { + + console.log(`Found reference links in CPS ${folderName}:`); + console.log(`WARNING: Reference link ${link.reference} in CPS ${folderName} is an relative link: ${link.url}.`); + + // Rewrite link to static folder + content = content.replace( + `[${link.reference}]`, + `[${link.reference}](${link.url})` + ); + } + }) + ); + } + + const mixedReferenceLinks = identifyMixedReferenceLinks(content); + if (mixedReferenceLinks.length > 0) { + mixedReferenceLinks.forEach(mixedLink => { + const matchedReference = referenceLinks.find(link => link.reference === mixedLink.reference && (link.url.indexOf("./") == 0 || link.url.indexOf("../") == 0)); + if (matchedReference) { + + console.log(`Found mixed reference links in CPS ${folderName}:`); + console.log(`WARNING: Mixed reference link [${mixedLink.text}][${mixedLink.reference}] is an relative mixed link: ${matchedReference.url}.`); + + content = content.replace(`[${mixedLink.text}][${mixedLink.reference}]`, `[${mixedLink.text}](${cip_repo_raw_base_url}${folderName}/${matchedReference.url})`); + } + }); + } + + const cip_resource = content.match(cip_regex); + if (cip_resource) { + await Promise.all( + cip_resource.map(async (r) => { + if (r.indexOf("http://") < 0 && r.indexOf("https://") < 0) { + + // Create modified file_names in case we want to store files + // with a different ending, like JSON files + const modified_file_name = r + .replace("](", "") + .replace(".png)", ".png") + .replace(".jpg)", ".jpg") + .replace(".jpeg)", ".jpeg") + .replace(".json)", ".json"); + + // Rewrite link to static folder + content = content.replace( + modified_file_name, + `../../../static/img/cps/${folderName}/${modified_file_name}` + ); + } + }) + ); + } const filePath = path.join(cps_target_folder, `${folderName}.md`); try { - await fs.promises.writeFile(filePath, newContentWithCorrectImagePaths); + await fs.promises.writeFile(filePath, content); console.log(`Updated ${filePath}`); } catch (error) { console.error(`Error updating ${filePath}:`, error.message); diff --git a/scripts/reusable.ts b/scripts/reusable.ts index af0c8a949b..e8530f1d26 100644 --- a/scripts/reusable.ts +++ b/scripts/reusable.ts @@ -20,3 +20,49 @@ export const preventH1Headline = (content: string, headline: string) => { export const getDocTag = (content: string, tag_name: string) => { return content.match(new RegExp(`(?<=${tag_name}: ).*`, "")); }; + +export const identifyReferenceLinks = (content: string) => { + // Regular expression to match reference-style links + const referenceLinkRegex = /\[([^\]]+)\]:\s*(\S+)/g; + const matches = []; + let match; + + // Loop through all matches and push them into the matches array + while ((match = referenceLinkRegex.exec(content)) !== null) { + matches.push({ + reference: match[1], // The reference name + url: match[2] // The URL + }); + } + + return matches; +}; + +// Identify mixed reference links in Markdown content +export const identifyMixedReferenceLinks = (content: string) => { + const mixedReferenceLinkRegex = /\[([^\]]+)\]\[([^\]]+)\]/g; + const matches = []; + let match; + + while ((match = mixedReferenceLinkRegex.exec(content)) !== null) { + matches.push({ + text: match[1], + reference: match[2], + }); + } + + return matches; +}; + +// Identify nested extended CIP links in Markdown content +export const identifyNestedExtendedCIPLinks = (content: string) => { + const nestedExtendedCIPRegex = /\(\.\/CIPs\/\d{4}\)/gm; + const matches = []; + let match; + + while ((match = nestedExtendedCIPRegex.exec(content)) !== null) { + matches.push(match[0]); + } + + return matches; +}; \ No newline at end of file