Static Site Generator

This project is a Static Site Generator written in Scala. It generates a static website by processing Markdown files, converting them into HTML pages, and syncing them with the server hosting the blog. The project uses modern web technologies like Bootstrap for styling and Highlight.js for syntax highlighting.

Project Structure:

blog/  
├── build.mill               
├── StaticSite/              
│   ├── src/  
│   │   ├── gen/  
│   │   │   └── Base.scala       
│   │   └── Generator.scala                    
├── sync/                      
│   ├── src/  
│   │   └── Sync.scala      
│   ├── conf/  
│   │   └── Util.scala      
│   ├── resources/      
│   │   └── conf.json           
├── agent/                     
│   ├── src/  
│   │   └── Agent.scala             
├── shared/                       
│   ├── src/  
│   │   ├── Shared.scala  
│   │   └── Rpc.scala                 
└── .gitignore          

Tech Stack:

How It Works:

//#1
val parser = Parser.Builder().build()
val renderer = HtmlRenderer.builder().build()

//#2
for((_, title, path) <- postInfo) {
   val document = parser.parse(os.read(path))
   val content = renderer.render(document)
   val date = getDate(path.toString)

   os.write(
      os.pwd / "html" / "posts" / titleToFileName(title),
      genBase(
         genNav(RelPath("..")),
         div(
            `class` := "container-fluid"
         )(
            h1(a(href := "../index.html")),
            h4(s"Last updated: $date"),
            raw(content)
         )
      )
   )
}
object Shared {
   def send[T: Writer](out: java.io.DataOutputStream, msg: T) = {
      val bytes = upickle.default.writeBinary(msg)
      out.writeInt(bytes.length)
      out.write(bytes)
      out.flush()
   }

   def receive[T: Reader](in: java.io.DataInputStream) = {
      val buf = new Array[Byte](in.readInt())
      in.readFully(buf)
      upickle.default.readBinary[T](buf)
   }
}
//#1
val agent = os.proc("ssh", remoteUser, "sudo", "-S", "./out.jar").spawn()

//#2
agent.stdin.writeLine(password)
agent.stdin.flush()

//#3
def callAgent[T: upickle.default.Reader](rpc: Rpc) = {
   Shared.send(agent.stdin.data, rpc)
   Shared.receive(agent.stdout.data)
}

//#4
callAgent[Purge](Purge(pathToServerHtml))

//#5
for(srcSubPth <- os.walk(os.pwd / "html")) {
   val subPath = srcSubPth.subRelativeTo(pathToLocalHtml)
   val destPath = pathToServerHtml / subPath

   if(os.isFile(srcSubPth)) callAgent[Copy](Copy(os.read.bytes(srcSubPth), destPath))
}

agent.destroy()
while true do {
   try {
      //#1
      val rpc = Shared.receive[Rpc](input)

      //#2
      rpc match
         case Purge(path) => Shared.send(output, os.remove.all(pathToHtml / path))
         case Copy(src, path) => Shared.send(output, os.write.over(pathToHtml / path, src, createFolders = true))
      
   } catch {
      case e: java.io.EOFException => System.exit(0)
   }
}