repos / pgit

static site generator for git
git clone https://github.com/picosh/pgit.git

commit
e19c0c4
parent
8014989
author
Eric Bower
date
2024-12-19 03:50:49 +0000 UTC
feat: `--root-relative` flag to change the url for href links
5 files changed,  +71, -53
M html/base.layout.tmpl
+3, -3
 1@@ -10,9 +10,9 @@
 2 
 3     {{template "meta" .}}
 4 
 5-    <link rel="stylesheet" href="/vars.css" />
 6-    <link rel="stylesheet" href="/smol.css" />
 7-    <link rel="stylesheet" href="/main.css" />
 8+    <link rel="stylesheet" href="{{.Repo.RootRelative}}vars.css" />
 9+    <link rel="stylesheet" href="{{.Repo.RootRelative}}smol.css" />
10+    <link rel="stylesheet" href="{{.Repo.RootRelative}}main.css" />
11   </head>
12   <body>
13     <header>{{template "header" .}}</header>
M html/commit.page.tmpl
+1, -1
1@@ -1,7 +1,7 @@
2 {{template "base" .}}
3 {{define "title"}}{{.Commit.Summary}} - {{.Repo.RepoName}}@{{.CommitID}}{{end}}
4 {{define "meta"}}
5-<link rel="stylesheet" href="/syntax.css" />
6+<link rel="stylesheet" href="{{.Repo.RootRelative}}syntax.css" />
7 {{end}}
8 
9 {{define "content"}}
M html/file.page.tmpl
+1, -1
1@@ -1,7 +1,7 @@
2 {{template "base" .}}
3 {{define "title"}}{{.Item.Path}}@{{.RevData.Name}}{{end}}
4 {{define "meta"}}
5-<link rel="stylesheet" href="/syntax.css" />
6+<link rel="stylesheet" href="{{.Repo.RootRelative}}syntax.css" />
7 {{end}}
8 
9 {{define "content"}}
M html/summary.page.tmpl
+1, -1
1@@ -2,7 +2,7 @@
2 
3 {{define "title"}}{{.Repo.RepoName}}{{if .Repo.Desc}}- {{.Repo.Desc}}{{end}}{{end}}
4 {{define "meta"}}
5-<link rel="stylesheet" href="/syntax.css" />
6+<link rel="stylesheet" href="{{.Repo.RootRelative}}syntax.css" />
7 {{end}}
8 
9 {{define "content"}}
M main.go
+65, -47
  1@@ -14,6 +14,7 @@ import (
  2 	"sort"
  3 	"strings"
  4 	"sync"
  5+	"time"
  6 	"unicode/utf8"
  7 
  8 	"github.com/alecthomas/chroma/v2"
  9@@ -52,6 +53,9 @@ type Config struct {
 10 	HomeURL  template.URL
 11 	CloneURL template.URL
 12 
 13+	// https://developer.mozilla.org/en-US/docs/Web/API/URL_API/Resolving_relative_references#root_relative
 14+	RootRelative string
 15+
 16 	// computed
 17 	// cache for skipping commits, trees, etc.
 18 	Cache map[string]bool
 19@@ -73,8 +77,9 @@ type RevInfo interface {
 20 
 21 // revision data
 22 type RevData struct {
 23-	id   string
 24-	name string
 25+	id     string
 26+	name   string
 27+	Config *Config
 28 }
 29 
 30 func (r *RevData) ID() string {
 31@@ -86,11 +91,11 @@ func (r *RevData) Name() string {
 32 }
 33 
 34 func (r *RevData) TreeURL() template.URL {
 35-	return getTreeURL(r)
 36+	return r.Config.getTreeURL(r)
 37 }
 38 
 39 func (r *RevData) LogURL() template.URL {
 40-	return getLogsURL(r)
 41+	return r.Config.getLogsURL(r)
 42 }
 43 
 44 type TagData struct {
 45@@ -427,7 +432,7 @@ func (c *Config) writeHTMLTreeFile(pageData *PageData, treeItem *TreeItem) strin
 46 			Contents: template.HTML(contents),
 47 			Item:     treeItem,
 48 		},
 49-		Subdir: getFileURL(pageData.RevData, d),
 50+		Subdir: getFileDir(pageData.RevData, d),
 51 	})
 52 	return readme
 53 }
 54@@ -494,8 +499,8 @@ func (c *Config) writeLogDiff(repo *git.Repository, pageData *PageData, commit *
 55 		CommitID:  getShortID(commitID),
 56 		Diff:      rnd,
 57 		Parent:    getShortID(commit.ParentID),
 58-		CommitURL: getCommitURL(commitID),
 59-		ParentURL: getCommitURL(commit.ParentID),
 60+		CommitURL: c.getCommitURL(commitID),
 61+		ParentURL: c.getCommitURL(commit.ParentID),
 62 	}
 63 
 64 	c.writeHtml(&WriteData{
 65@@ -506,13 +511,13 @@ func (c *Config) writeLogDiff(repo *git.Repository, pageData *PageData, commit *
 66 	})
 67 }
 68 
 69-func getSummaryURL() template.URL {
 70-	url := "/index.html"
 71+func (c *Config) getSummaryURL() template.URL {
 72+	url := c.RootRelative + "index.html"
 73 	return template.URL(url)
 74 }
 75 
 76-func getRefsURL() template.URL {
 77-	url := "/refs.html"
 78+func (c *Config) getRefsURL() template.URL {
 79+	url := c.RootRelative + "refs.html"
 80 	return template.URL(url)
 81 }
 82 
 83@@ -537,24 +542,32 @@ func getFileBaseDir(info RevInfo) string {
 84 	return filepath.Join(getTreeBaseDir(info), "item")
 85 }
 86 
 87-func getFileURL(info RevInfo, fname string) string {
 88+func getFileDir(info RevInfo, fname string) string {
 89 	return filepath.Join(getFileBaseDir(info), fname)
 90 }
 91 
 92-func getTreeURL(info RevInfo) template.URL {
 93-	dir := getTreeBaseDir(info)
 94-	url := filepath.Join(dir, "index.html")
 95+func (c *Config) getFileURL(info RevInfo, fname string) template.URL {
 96+	return c.compileURL(getFileBaseDir(info), fname)
 97+}
 98+
 99+func (c *Config) compileURL(dir, fname string) template.URL {
100+	purl := c.RootRelative + strings.TrimPrefix(dir, "/")
101+	url := filepath.Join(purl, fname)
102 	return template.URL(url)
103 }
104 
105-func getLogsURL(info RevInfo) template.URL {
106+func (c *Config) getTreeURL(info RevInfo) template.URL {
107+	dir := getTreeBaseDir(info)
108+	return c.compileURL(dir, "index.html")
109+}
110+
111+func (c *Config) getLogsURL(info RevInfo) template.URL {
112 	dir := getLogBaseDir(info)
113-	url := filepath.Join(dir, "index.html")
114-	return template.URL(url)
115+	return c.compileURL(dir, "index.html")
116 }
117 
118-func getCommitURL(commitID string) template.URL {
119-	url := fmt.Sprintf("/commits/%s.html", commitID)
120+func (c *Config) getCommitURL(commitID string) template.URL {
121+	url := fmt.Sprintf("%scommits/%s.html", c.RootRelative, commitID)
122 	return template.URL(url)
123 }
124 
125@@ -562,8 +575,8 @@ func (c *Config) getURLs() *SiteURLs {
126 	return &SiteURLs{
127 		HomeURL:    c.HomeURL,
128 		CloneURL:   c.CloneURL,
129-		RefsURL:    getRefsURL(),
130-		SummaryURL: getSummaryURL(),
131+		RefsURL:    c.getRefsURL(),
132+		SummaryURL: c.getSummaryURL(),
133 	}
134 }
135 
136@@ -596,8 +609,9 @@ func (c *Config) writeRepo() *BranchOutput {
137 		}
138 
139 		data := &RevData{
140-			id:   fullRevID,
141-			name: revName,
142+			id:     fullRevID,
143+			name:   revName,
144+			Config: c,
145 		}
146 
147 		if first == nil {
148@@ -673,8 +687,9 @@ func (c *Config) writeRepo() *BranchOutput {
149 	// use the first revision in our list to generate
150 	// the root summary, logs, and tree the user can click
151 	revData := &RevData{
152-		id:   first.ID(),
153-		name: first.Name(),
154+		id:     first.ID(),
155+		name:   first.Name(),
156+		Config: c,
157 	}
158 
159 	data := &PageData{
160@@ -699,6 +714,7 @@ type TreeWalker struct {
161 	HideTreeLastCommit bool
162 	PageData           *PageData
163 	Repo               *git.Repository
164+	Config             *Config
165 }
166 
167 type Breadcrumb struct {
168@@ -712,11 +728,9 @@ func (tw *TreeWalker) calcBreadcrumbs(curpath string) []*Breadcrumb {
169 		return []*Breadcrumb{}
170 	}
171 	parts := strings.Split(curpath, string(os.PathSeparator))
172-	rootURL := template.URL(
173-		filepath.Join(
174-			getTreeBaseDir(tw.PageData.RevData),
175-			"index.html",
176-		),
177+	rootURL := tw.Config.compileURL(
178+		getTreeBaseDir(tw.PageData.RevData),
179+		"index.html",
180 	)
181 
182 	crumbs := make([]*Breadcrumb, len(parts)+1)
183@@ -727,9 +741,11 @@ func (tw *TreeWalker) calcBreadcrumbs(curpath string) []*Breadcrumb {
184 
185 	cur := ""
186 	for idx, d := range parts {
187+		crumb := filepath.Join(getFileBaseDir(tw.PageData.RevData), cur, d)
188+		crumbUrl := tw.Config.compileURL(crumb, "index.html")
189 		crumbs[idx+1] = &Breadcrumb{
190 			Text: d,
191-			URL:  template.URL(filepath.Join(getFileBaseDir(tw.PageData.RevData), cur, d, "index.html")),
192+			URL:  crumbUrl,
193 		}
194 		if idx == len(parts)-1 {
195 			crumbs[idx+1].IsLast = true
196@@ -775,7 +791,7 @@ func (tw *TreeWalker) NewTreeItem(entry *git.TreeEntry, curpath string, crumbs [
197 		Name:   entry.Name(),
198 		Path:   fname,
199 		Entry:  entry,
200-		URL:    template.URL(getFileURL(tw.PageData.RevData, fname)),
201+		URL:    tw.Config.getFileURL(tw.PageData.RevData, fname),
202 		Crumbs: crumbs,
203 	}
204 
205@@ -794,29 +810,28 @@ func (tw *TreeWalker) NewTreeItem(entry *git.TreeEntry, curpath string, crumbs [
206 		if len(lastCommits) > 0 {
207 			lc = lastCommits[0]
208 		}
209-		item.CommitURL = getCommitURL(lc.ID.String())
210+		item.CommitURL = tw.Config.getCommitURL(lc.ID.String())
211 		item.CommitID = getShortID(lc.ID.String())
212 		item.Summary = lc.Summary()
213-		item.When = lc.Author.When.Format("02 Jan 06")
214+		item.When = lc.Author.When.Format(time.DateOnly)
215 		item.Author = lc.Author
216 	}
217 
218-	fpath := getFileURL(
219-		tw.PageData.RevData,
220-		fmt.Sprintf("%s.html", fname),
221-	)
222+	fpath := tw.Config.getFileURL(tw.PageData.RevData, fmt.Sprintf("%s.html", fname))
223 	if typ == git.ObjectTree {
224 		item.IsDir = true
225-		fpath = filepath.Join(
226-			getFileBaseDir(tw.PageData.RevData),
227-			curpath,
228-			entry.Name(),
229+		fpath = tw.Config.compileURL(
230+			filepath.Join(
231+				getFileBaseDir(tw.PageData.RevData),
232+				curpath,
233+				entry.Name(),
234+			),
235 			"index.html",
236 		)
237 	} else if typ == git.ObjectBlob {
238 		item.Icon = FilenameToDevIcon(item.Name)
239 	}
240-	item.URL = template.URL(fpath)
241+	item.URL = fpath
242 
243 	return item
244 }
245@@ -926,11 +941,11 @@ func (c *Config) writeRevision(repo *git.Repository, pageData *PageData, refs []
246 			}
247 			logs = append(logs, &CommitData{
248 				ParentID:   parentID,
249-				URL:        getCommitURL(commit.ID.String()),
250+				URL:        c.getCommitURL(commit.ID.String()),
251 				ShortID:    getShortID(commit.ID.String()),
252 				SummaryStr: commit.Summary(),
253 				AuthorStr:  commit.Author.Name,
254-				WhenStr:    commit.Author.When.Format("02 Jan 06"),
255+				WhenStr:    commit.Author.When.Format(time.DateOnly),
256 				Commit:     commit,
257 				Refs:       tags,
258 			})
259@@ -954,6 +969,7 @@ func (c *Config) writeRevision(repo *git.Repository, pageData *PageData, refs []
260 	entries := make(chan *TreeItem)
261 	subtrees := make(chan *TreeRoot)
262 	tw := &TreeWalker{
263+		Config:   c,
264 		PageData: pageData,
265 		Repo:     repo,
266 		treeItem: entries,
267@@ -1035,12 +1051,13 @@ func style(theme chroma.Style) string {
268 func main() {
269 	var outdir = flag.String("out", "./public", "output directory")
270 	var rpath = flag.String("repo", ".", "path to git repo")
271-	var revsFlag = flag.String("revs", "HEAD", "list of revs to generate logs and tree (e.g. main,v1,c69f86f,HEAD")
272+	var revsFlag = flag.String("revs", "HEAD", "list of revs to generate logs and tree (e.g. main,v1,c69f86f,HEAD)")
273 	var themeFlag = flag.String("theme", "dracula", "theme to use for site")
274 	var labelFlag = flag.String("label", "", "pretty name for the subdir where we create the repo, default is last folder in --repo")
275 	var cloneFlag = flag.String("clone-url", "", "git clone URL")
276 	var homeFlag = flag.String("home-url", "", "URL for breadcumbs to get to list of repositories")
277 	var descFlag = flag.String("desc", "", "description for repo")
278+	var rootRelativeFlag = flag.String("root-relative", "/", "html root relative")
279 	var maxCommitsFlag = flag.Int("max-commits", 0, "maximum number of commits to generate")
280 	var hideTreeLastCommitFlag = flag.Bool("hide-tree-last-commit", false, "dont calculate last commit for each file in the tree")
281 
282@@ -1084,6 +1101,7 @@ func main() {
283 		Desc:               *descFlag,
284 		MaxCommits:         *maxCommitsFlag,
285 		HideTreeLastCommit: *hideTreeLastCommitFlag,
286+		RootRelative:       *rootRelativeFlag,
287 		Formatter:          formatter,
288 	}
289 	config.Logger.Info("config", "config", config)