microsoft/TypeAgent
Publicmirrored fromhttps://github.com/microsoft/TypeAgentAvailable
docs/scripts/update-links.js
153lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | // Normalizes file paths to use forward slashes consistently |
| 5 | function normalizePath(filePath) { |
| 6 | return filePath ? filePath.replace(/\\/g, "/") : ""; |
| 7 | } |
| 8 | |
| 9 | /** |
| 10 | * Updates links in content to point to GitHub repository |
| 11 | * This relies on Eleventy's inputPath property to get the original Markdown path |
| 12 | * |
| 13 | * @param {string} content - The HTML content |
| 14 | * @param {string} outputPath - The output HTML file path |
| 15 | * @param {string} inputPath - The original Markdown file path |
| 16 | * @param {string} repoUrl - The GitHub repository URL |
| 17 | * @param {string} defaultBranch - The default branch name |
| 18 | * @return {string} Updated content |
| 19 | */ |
| 20 | function updateLinks( |
| 21 | content, |
| 22 | outputPath, |
| 23 | inputPath, |
| 24 | repoUrl, |
| 25 | defaultBranch = "main" |
| 26 | ) { |
| 27 | if (!repoUrl) { |
| 28 | repoUrl = "https://github.com/microsoft/TypeAgent"; |
| 29 | } |
| 30 | |
| 31 | // Normalize paths |
| 32 | const normalizedInputPath = normalizePath(inputPath); |
| 33 | const normalizedOutputPath = normalizePath(outputPath); |
| 34 | |
| 35 | console.log(`Processing file: ${normalizedOutputPath}`); |
| 36 | console.log(`Original source: ${normalizedInputPath}`); |
| 37 | |
| 38 | // Process HTML links |
| 39 | const externalLinkPattern = |
| 40 | /<a\s+(?:[^>]*?\s+)?href=["']([^"']+)["']([^>]*)>([^<]*)<\/a>/g; |
| 41 | |
| 42 | return content.replace( |
| 43 | externalLinkPattern, |
| 44 | (match, linkPath, attributes, linkText) => { |
| 45 | // Normalize link path |
| 46 | const normalizedLinkPath = normalizePath(linkPath); |
| 47 | |
| 48 | // Skip if the link is internal (starts with #), a web URL, or a site-relative path |
| 49 | if ( |
| 50 | normalizedLinkPath.startsWith("#") || |
| 51 | normalizedLinkPath.startsWith("http") || |
| 52 | normalizedLinkPath.startsWith("/docs/") || |
| 53 | normalizedLinkPath.startsWith("/content/") |
| 54 | ) { |
| 55 | return match; |
| 56 | } |
| 57 | |
| 58 | // Skip image files |
| 59 | if (/\.(jpeg|jpg|gif|png|svg)$/.test(normalizedLinkPath)) { |
| 60 | return match; |
| 61 | } |
| 62 | |
| 63 | // Only process links that likely point outside the docs directory |
| 64 | // This typically means they start with ../ or are a path without a leading / |
| 65 | if ( |
| 66 | normalizedLinkPath.startsWith("../") || |
| 67 | (!normalizedLinkPath.startsWith("/") && |
| 68 | !normalizedLinkPath.startsWith(".")) |
| 69 | ) { |
| 70 | try { |
| 71 | // Use the original Markdown file path for resolution |
| 72 | const repoPath = resolvePathFromMarkdown( |
| 73 | normalizedLinkPath, |
| 74 | normalizedInputPath |
| 75 | ); |
| 76 | |
| 77 | // Determine if this is a file or directory (assume directory if no extension) |
| 78 | const isDirectory = !repoPath.includes("."); |
| 79 | |
| 80 | // Create the appropriate GitHub URL |
| 81 | const githubPath = isDirectory |
| 82 | ? `${repoUrl}/tree/${defaultBranch}/${repoPath}` |
| 83 | : `${repoUrl}/blob/${defaultBranch}/${repoPath}`; |
| 84 | |
| 85 | console.log( |
| 86 | `Replacing link: ${normalizedLinkPath} with ${githubPath}` |
| 87 | ); |
| 88 | |
| 89 | // Return the updated link |
| 90 | return `<a href="${githubPath}"${attributes}>${linkText}</a>`; |
| 91 | } catch (err) { |
| 92 | console.error( |
| 93 | `Error processing link ${normalizedLinkPath}: ${err.message}` |
| 94 | ); |
| 95 | return match; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | return match; |
| 100 | } |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Resolves a relative path from a Markdown file to its repository path |
| 106 | * |
| 107 | * @param {string} relativePath - The relative path in the link |
| 108 | * @param {string} markdownFilePath - The original Markdown file path |
| 109 | * @returns {string} The resolved path relative to the repository root |
| 110 | */ |
| 111 | function resolvePathFromMarkdown(relativePath, markdownFilePath) { |
| 112 | try { |
| 113 | const lastSlashIndex = markdownFilePath.lastIndexOf("/"); |
| 114 | const markdownDir = |
| 115 | lastSlashIndex !== -1 |
| 116 | ? markdownFilePath.substring(0, lastSlashIndex) |
| 117 | : ""; |
| 118 | |
| 119 | const markdownDirParts = markdownDir ? markdownDir.split("/") : []; |
| 120 | const relativePathParts = relativePath.split("/"); |
| 121 | |
| 122 | const resultParts = [...markdownDirParts]; |
| 123 | |
| 124 | // Process each part of the relative path |
| 125 | for (const part of relativePathParts) { |
| 126 | if (part === "..") { |
| 127 | // Go up one directory |
| 128 | if (resultParts.length > 0) { |
| 129 | resultParts.pop(); |
| 130 | } |
| 131 | } else if (part !== ".") { |
| 132 | resultParts.push(part); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | // Join the path |
| 137 | const resolvedPath = resultParts.join("/"); |
| 138 | |
| 139 | const docsIndex = resolvedPath.indexOf("/docs/"); |
| 140 | if (docsIndex !== -1) { |
| 141 | // If the path contains /docs/, everything before that is the repo root |
| 142 | const repoRoot = resolvedPath.substring(0, docsIndex); |
| 143 | return resolvedPath.substring(repoRoot.length + 1); // +1 for the leading slash |
| 144 | } |
| 145 | |
| 146 | return resolvedPath; |
| 147 | } catch (err) { |
| 148 | console.error(`Error resolving path: ${err.message}`); |
| 149 | return relativePath; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | module.exports = { updateLinks, normalizePath, resolvePathFromMarkdown }; |
| 154 | |