summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Hurst <ark@mansus.space>2025-09-29 18:50:00 +0200
committerJulian Hurst <ark@mansus.space>2025-09-29 18:50:00 +0200
commite3552f592ba5bce3fa3243166679fed986b6b949 (patch)
treea0eca31771a17dc955d769862d7bdd12d8100f0c
downloadssg-e3552f592ba5bce3fa3243166679fed986b6b949.tar.gz
Initial commit
-rw-r--r--.gitignore2
-rw-r--r--base.html9
-rwxr-xr-xconverter/md2html3
-rwxr-xr-xconverter/skipmetadatabin0 -> 2288264 bytes
-rw-r--r--converter/skipmetadata.go30
-rw-r--r--go.mod3
-rw-r--r--head.html3
-rw-r--r--index.html15
-rw-r--r--posts/chickens.md8
-rw-r--r--posts/rabbits.md6
-rw-r--r--ssg.go261
11 files changed, 340 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b857cda
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+public
+ssg
diff --git a/base.html b/base.html
new file mode 100644
index 0000000..280cb7c
--- /dev/null
+++ b/base.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ {{template "head"}}
+</head>
+<body>
+ {{template "body" .}}
+</body>
+</html>
diff --git a/converter/md2html b/converter/md2html
new file mode 100755
index 0000000..331f348
--- /dev/null
+++ b/converter/md2html
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+md2html
diff --git a/converter/skipmetadata b/converter/skipmetadata
new file mode 100755
index 0000000..c683e66
--- /dev/null
+++ b/converter/skipmetadata
Binary files differ
diff --git a/converter/skipmetadata.go b/converter/skipmetadata.go
new file mode 100644
index 0000000..869604e
--- /dev/null
+++ b/converter/skipmetadata.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "bufio"
+)
+
+func main() {
+ if len(os.Args) != 2 {
+ fmt.Fprintf(os.Stderr, "USAGE: %s file\n", os.Args[0])
+ }
+ f, err := os.Open(os.Args[1])
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer f.Close()
+ sc := bufio.NewScanner(f)
+ doPrint := false
+ for sc.Scan() {
+ line := sc.Text()
+ if doPrint {
+ fmt.Println(line)
+ }
+ if line == "----" {
+ doPrint = true
+ }
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f4bd324
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module ssg
+
+go 1.25.1
diff --git a/head.html b/head.html
new file mode 100644
index 0000000..236eaf4
--- /dev/null
+++ b/head.html
@@ -0,0 +1,3 @@
+{{define "head"}}
+ <link rel="stylesheet" href="/static/style.css">
+{{end}}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..d353e18
--- /dev/null
+++ b/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ {{template "head"}}
+</head>
+<body>
+ {{define "body"}}
+ <h1>My blog</h1>
+ {{range .}}
+ <a href="{{.Path}}">{{.Metadata.Name}}</a>
+ <p>{{.Metadata.Description}}</p>
+ {{end}}
+ {{end}}
+</body>
+</html>
diff --git a/posts/chickens.md b/posts/chickens.md
new file mode 100644
index 0000000..f7f8d78
--- /dev/null
+++ b/posts/chickens.md
@@ -0,0 +1,8 @@
+name=cheese
+description=chickens are amazing
+----
+# Chickens are great
+
+We all like chickens here, aren't they great?!!
+
+![chicken](https://cs-tf.com/wp-content/uploads/2022/08/Wild-Chickens-and-Habitat-Space.jpg)
diff --git a/posts/rabbits.md b/posts/rabbits.md
new file mode 100644
index 0000000..9f44d18
--- /dev/null
+++ b/posts/rabbits.md
@@ -0,0 +1,6 @@
+name=Rabbits are incredible
+description=I really like rabbits and think they're awesome. One of my best friends is a Rabbit although he's technically a hare...
+----
+# Rabbits!!!!
+
+![Rabbit](https://www.hdwallpapersfreedownload.com/uploads/large/animals/rabbit-1080p.jpg)
diff --git a/ssg.go b/ssg.go
new file mode 100644
index 0000000..743369d
--- /dev/null
+++ b/ssg.go
@@ -0,0 +1,261 @@
+package main
+
+import (
+ "slices"
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "strings"
+ "path/filepath"
+ //"flag"
+ "html/template"
+ "time"
+)
+
+type post struct {
+ InPath string
+ OutPath string
+ ModTime time.Time
+}
+
+type ParsedPost struct {
+ Path string
+ Metadata PostMetadata
+}
+
+type PostMetadata struct {
+ Name string
+ Description string
+}
+
+func buildTemplate(f string, w io.Writer, data interface{}) error {
+ tmpl, err := template.ParseFiles("base.html", "head.html", f)
+ if err != nil {
+ return err
+ }
+ err = tmpl.Execute(w, data)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func main() {
+ args := os.Args
+ if len(args) != 2 {
+ fmt.Fprintf(os.Stderr, "USAGE: %s [postsfolder]", args[0])
+ os.Exit(1)
+ }
+
+
+ postsOutDir := "public/posts/"
+
+ err := os.MkdirAll(postsOutDir, 0o777)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ f, err := os.Open(args[1])
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer f.Close()
+
+ entries, err := f.ReadDir(0)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ var files []post = nil
+ for _, entry := range entries {
+ name := entry.Name()
+ outName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
+ fi, err := entry.Info()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ p := post {
+ InPath: filepath.Join(args[1], name),
+ OutPath: filepath.Join(postsOutDir, outName),
+ ModTime: fi.ModTime(),
+ }
+ files = append(files, p)
+ }
+
+ slices.SortFunc(files, func(a, b post) int {
+ if a.ModTime.After(b.ModTime) {
+ return -1
+ } else {
+ return 1
+ }
+ })
+
+ pposts, err := parseMetadata(files)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ err = md2html(files)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ idx, err := os.Create("public/index.html")
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ //var data []string = nil
+ //for _, post := range files {
+ // data = append(data, filepath.Join("posts", filepath.Base(post.OutPath)))
+ //}
+ for _, ppost := range pposts {
+ fmt.Fprintf(os.Stderr, "name: %s, description: %s\n", ppost.Metadata.Name, ppost.Metadata.Description)
+ }
+ err = buildTemplate("index.html", idx, pposts)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+}
+
+func parseMetadata(files []post) ([]ParsedPost, error) {
+ var pposts []ParsedPost = nil
+ for _, file := range files {
+
+ f, err := os.Open(file.InPath)
+ if err != nil {
+ return pposts, err
+ }
+ defer f.Close()
+
+ sc := bufio.NewScanner(f)
+
+ lnb := 1
+ var metadata PostMetadata = PostMetadata {}
+ for sc.Scan() {
+ line := sc.Text()
+ if line == "----" {
+ break
+ }
+ spl := strings.Split(line, "=")
+ if len(spl) != 2 {
+ return pposts, fmt.Errorf("Invalid Metadata: %s:%d", file.InPath, lnb)
+ }
+ switch spl[0] {
+ case "name":
+ metadata.Name = spl[1]
+ case "description":
+ metadata.Description = spl[1]
+ default:
+ fmt.Fprintf(os.Stderr, "Invalid key: %s (%s:%d)\n", spl[0], file.InPath, lnb)
+ }
+ lnb += 1
+ }
+ pposts = append(pposts, ParsedPost {
+ Path: filepath.Join("posts", filepath.Base(file.OutPath)),
+ Metadata: metadata,
+ })
+ }
+ return pposts, nil
+}
+
+func md2html(files []post) error {
+ for _, file := range files {
+ //err = os.WriteFile(file.OutPath, out, 0600)
+ //if err != nil {
+ // return err
+ //}
+ out, err := execcmd(file)
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(out))
+ tmpl, err := template.New("base.html").ParseFiles("base.html", "head.html")
+ if err != nil {
+ return err
+ }
+ _, err = tmpl.New("body").Parse(string(out))
+ if err != nil {
+ return err
+ }
+ f, err := os.Create(file.OutPath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ err = tmpl.Execute(f, nil)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func execcmd(file post) ([]byte, error) {
+ cmd := exec.Command("converter/md2html", file.InPath)
+
+ inPipe, err := cmd.StdinPipe()
+ if err != nil {
+ return nil, err
+ }
+ outPipe, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ errPipe, err := cmd.StderrPipe()
+ if err != nil {
+ return nil, err
+ }
+
+
+ go func() {
+ defer inPipe.Close()
+ f, _ := os.Open(file.InPath)
+ defer f.Close()
+ sc := bufio.NewScanner(f)
+ doPrint := false
+ for sc.Scan() {
+ line := sc.Text()
+ if doPrint {
+ io.WriteString(inPipe, line + "\n")
+ }
+ if line == "----" {
+ doPrint = true
+ }
+ }
+ io.Copy(inPipe, f)
+ }()
+
+ fmt.Fprintln(os.Stderr, "stdout")
+
+ var out []byte = nil
+ go func() {
+ out, _ = io.ReadAll(outPipe)
+ }()
+
+ fmt.Fprintln(os.Stderr, "stderr")
+
+ var cmdErr []byte = nil
+ go func() {
+ cmdErr, _ = io.ReadAll(errPipe)
+ }()
+
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ if err := cmd.Wait(); err != nil {
+ return nil, fmt.Errorf("%s\nstderr: %s\n", err, string(cmdErr))
+ }
+ return out, nil
+}