All files / src/util md-files.ts

60% Statements 36/60
26.08% Branches 6/23
72.22% Functions 13/18
61.81% Lines 34/55

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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 1385x 5x 5x 5x   5x 5x   5x                   45x               5x 6x           6x     21x 21x     21x     21x   21x 21x       3x 3x 3x                       21x       21x 21x 21x 63x   21x                         3x 21x 21x 21x       3x                                                 3x                             3x 3x      
import Debug from "debug";
import {FSWatcher, readdirSync, readFileSync, watch} from "fs";
import path, {join} from "path";
import showdown from "showdown";
import {RhizomeNode} from "../node";
const {Converter} = showdown;
const debug = Debug('rz:md-files');
 
const docConverter = new Converter({
  completeHTMLDocument: true,
  simpleLineBreaks: false,
  tables: true,
  tasklists: true
});
 
export type Markdown = string;
export type Html = string;
 
export const htmlDocFromMarkdown = (md: Markdown): Html => docConverter.makeHtml(md);
 
type mdFileInfo = {
  name: string,
  md: string,
  html: string
};
 
export class MDFiles {
  files = new Map<string, mdFileInfo>();
  readme?: mdFileInfo;
  dirWatcher?: FSWatcher;
  readmeWatcher?: FSWatcher;
  latestIndexHtml?: Html;
 
  constructor(readonly rhizomeNode: RhizomeNode) {}
 
  readFile(name: string) {
    const md = readFileSync(join('./markdown', `${name}.md`)).toString();
    let m = "";
 
    // Add title and render the markdown
    m += `# File: [${name}](/html/${name})\n\n---\n\n${md}`;
 
    // Add footer with the nav menu
    m += `\n\n---\n\n${this.generateIndex()}`;
 
    const html = htmlDocFromMarkdown(m);
    this.files.set(name, {name, md, html});
  }
 
  readReadme() {
    const md = readFileSync('./README.md').toString();
    const html = htmlDocFromMarkdown(md);
    this.readme = {name: 'README', md, html};
  }
 
  getReadmeHTML() {
    return this.readme?.html;
  }
 
  getHtml(name: string): string | undefined {
    return this.files.get(name)?.html;
  }
 
  list(): string[] {
    return Array.from(this.files.keys());
  }
 
  generateIndex(): Markdown {
    let md = `# [Index](/html)\n\n`;
    md += `[README](/html/README)\n\n`;
    for (const name of this.list()) {
      md += `- [${name}](/html/${name})\n`;
    }
    return htmlDocFromMarkdown(md);
  }
 
  get indexHtml(): Html {
    Iif (!this.latestIndexHtml) {
      this.latestIndexHtml = this.generateIndex();
    }
    return this.latestIndexHtml;
  }
 
  readDir() {
    // Read list of markdown files from directory and
    // render each markdown file as html
    readdirSync('./markdown/')
      .filter((f) => f.endsWith('.md'))
      .map((name) => path.parse(name).name)
      .forEach((name) => this.readFile(name));
  }
 
  watchDir() {
    this.dirWatcher = watch('./markdown', null, (eventType, filename) => {
      Iif (!filename) return;
      Iif (!filename.endsWith(".md")) return;
 
      const name = path.parse(filename).name;
 
      switch (eventType) {
        case 'rename': {
          debug(`[${this.rhizomeNode.config.peerId}]`, `File ${name} renamed`);
          // Remove it from memory and re-scan everything
          this.files.delete(name);
          this.readDir();
          break;
        }
        case 'change': {
          debug(`[${this.rhizomeNode.config.peerId}]`, `File ${name} changed`);
          // Re-read this file
          this.readFile(name)
          break;
        }
      }
    });
  }
 
  watchReadme() {
    this.readmeWatcher = watch('./README.md', null, (eventType, filename) => {
      Iif (!filename) return;
 
      switch (eventType) {
        case 'change': {
          debug(`[${this.rhizomeNode.config.peerId}]`, `README file changed`);
          // Re-read this file
          this.readReadme()
          break;
        }
      }
    });
  }
 
  stop() {
    this.dirWatcher?.close();
    this.readmeWatcher?.close();
  }
}