All files / scripts applyFallbackDocScan.js

76.47% Statements 26/34
81.25% Branches 26/32
100% Functions 1/1
74.19% Lines 23/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61  1x 1x   1x                       20x 20x 19x 19x 18x   18x 17x 17x 17x 14x 13x 13x     13x 13x         13x 13x 13x         20x     1x   1x                      
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
 
const FILE = path.join(__dirname, '..', 'public', 'projects.json');
 
/**
 * Regex-scans README text for doc-like markdown links and attaches the first match
 * as repoDocs.apiDocumentation. Only runs on nodes that have no repoDocs yet, so
 * structured extraction results are never overwritten.
 *
 * @param {object[]} nodes - Project nodes from projects.json
 * @param {Function} normalizeTitle - Title sanitizer from fetchProjects
 * @returns {object[]} The same nodes array, mutated in place
 */
function scanNodes(nodes, normalizeTitle) {
  for (const node of nodes) {
    if (node.repoDocs) continue;
    try {
      const txt = (node.object && node.object.text) || '';
      const linkRe = /\[([^\]]+)\]\((https?:\/\/[^)\s]+|\.\/[^)\s]+|\/[^)\s]+|[^)]+\.md)\)/ig;
      let m;
      while ((m = linkRe.exec(txt)) !== null) {
        const label = (m[1] || '').trim();
        let href = (m[2] || '').trim();
        if (/localhost|127\.0\.0\.1|docker/i.test(href)) continue;
        if (/docs?|api|redoc|openapi|swagger|reDoc|documentation|api docs/i.test(label + ' ' + href)) {
          if (!/^https?:\/\//i.test(href)) href = href.replace(/^\.?\//,'').replace(/^\//,'');
          const absolute = /^https?:\/\//i.test(href)
            ? href
            : `https://raw.githubusercontent.com/keglev/${node.name}/main/${href}`;
          node.repoDocs = node.repoDocs || {};
          node.repoDocs.apiDocumentation = node.repoDocs.apiDocumentation || {
            title: label || 'API Documentation',
            link: absolute,
            description: '',
          };
          node.docsLink = node.docsLink || absolute;
          node.docsTitle = node.docsTitle || normalizeTitle(label) || 'Documentation';
          break;
        }
      }
    } catch (e) { /* ignore */ }
  }
  return nodes;
}
 
module.exports = { scanNodes, FILE };
 
Iif (require.main === module) {
  if (!fs.existsSync(FILE)) {
    console.error('public/projects.json not found');
    process.exit(1);
  }
  const normalizeTitle = require('./fetchProjects.js').normalizeTitle;
  const nodes = JSON.parse(fs.readFileSync(FILE, 'utf8'));
  scanNodes(nodes, normalizeTitle);
  fs.writeFileSync(FILE, JSON.stringify(nodes, null, 2), 'utf8');
  console.log('Applied fallback doc-scan and wrote', FILE);
}