arnaucube.com/public/blogo.html
public/blogo.htmlRaw
1<!DOCTYPE html>
2<html lang="en">
3
4 <head>
5 <meta name="description" content="How has this blog been made? In this post we will see how to develop a minimalistic static blog template engine with Go." />
6 <meta charset="utf-8">
7 <title>Static blog template engine implementation in Go - ArnauCube - Blog</title>
8 <meta name="title" content="Static blog template engine implementation in Go - ArnauCube - Blog">
9 <meta name="description" content="How has this blog been made? In this post we will see how to develop a minimalistic static blog template engine with Go.">
10
11 <meta property="og:title" content="Static blog template engine implementation in Go - ArnauCube - Blog" />
12 <meta property="og:description" content="How has this blog been made? In this post we will see how to develop a minimalistic static blog template engine with Go." />
13 <meta property="og:url" content="https://arnaucube.com/blog/blogo.html" />
14 <meta property="og:type" content="article" />
15 <meta property="og:image" content="https://arnaucube.com/blog/" />
16 <meta name="twitter:title" content="Static blog template engine implementation in Go - ArnauCube - Blog">
17 <meta name="twitter:description" content="How has this blog been made? In this post we will see how to develop a minimalistic static blog template engine with Go.">
18 <meta name="twitter:image" content="https://arnaucube.com/blog/">
19 <meta name="twitter:card" content="summary_large_image">
20 <meta name="author" content="arnaucube">
21
22 <meta name="viewport" content="width=device-width, initial-scale=1">
23
24 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
25 <link rel="stylesheet" href="css/style.css">
26
27 <!-- highlightjs -->
28 <!-- <link rel="stylesheet" href="js/highlightjs/atom-one-dark.css"> -->
29 <link rel="stylesheet" href="js/highlightjs/gruvbox-dark.css">
30 <script src="js/highlightjs/highlight.pack.js"></script>
31
32 <!-- katex -->
33 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous">
34 </head>
35
36 <body>
37
38 <!-- o_gradient_background" -->
39 <nav id="mainNav" class="navbar navbar-default navbar-fixed-top"
40 style="height:50px;font-size:130%;">
41 <div class="container">
42 <a href="/blog" style="color:#000;">Blog index</a>
43 <a href="/" style="color:#000;float:right;">arnaucube.com</a>
44 </div>
45 <img style="height:5px; width:100%; margin-top:8px;" src="img/gradient-line.jpg" />
46 </nav>
47
48
49
50 <div class="container" style="margin-top:40px;max-width:800px;">
51 <h1>Static blog template engine implementation in Go</h1>
52
53<p><em>2017-12-26</em></p>
54
55<p>Some days ago, I decided to start this blog, to put there all the thoughts and ideas that goes through my mind. After some research, I’ve found some interesting projects, but with a lot of features that I don’t need to use. So I decided to write my own minimalistic static blog template engine with Go lang.</p>
56
57<p>This is how I made <a href="https://github.com/arnaucube/blogo">blogo</a> the blog static template engine to do this blog.</p>
58
59<h3>Static blog template engine?</h3>
60
61<p>The main idea is to be able to write the blog posts in Markdown files, and with the template engine, output the HTML files ready to upload in the web hosting server.</p>
62
63<h2>Structure</h2>
64
65<p>What we wan to have in the input is:</p>
66
67<pre><code>/blogo
68 /input
69 /css
70 mycss.css
71 /img
72 post01_image.png
73 index.html
74 postThumbTemplate.html
75 post01.md
76 post01thumb.md
77 post02.md
78 post02thumb.md
79</code></pre>
80
81<h4>blogo.json</h4>
82
83<p>This is the file that have the configuration that will be read by the Go script.</p>
84
85<pre><code class="language-json">{
86 "title": "my blog",
87 "indexTemplate": "index.html",
88 "postThumbTemplate": "postThumbTemplate.html",
89 "posts": [
90 {
91 "thumb": "post01thumb.md",
92 "md": "post01.md"
93 },
94 {
95 "thumb": "post02thumb.md",
96 "md": "post02.md"
97 }
98 ],
99 "copyRaw": [
100 "css",
101 "js"
102 ]
103}
104</code></pre>
105
106<p>The <em>copyRaw</em> element, will be all the directories to copy raw to the output.</p>
107
108<h4>index.html</h4>
109
110<p>This is the file that will be used as the template for the main page and also for the posts pages.</p>
111
112<pre><code class="language-html"><!DOCTYPE html>
113<html>
114<head>
115 <title>[blogo-title]</title>
116</head>
117<body>
118 <div>
119 [blogo-content]
120 </div>
121</body>
122</html>
123</code></pre>
124
125<p>As we can see, we just need to define the html file, and in the title define the <em>[blogo-title]</em>, and in the content place the <em>[blogo-content]</em>.</p>
126
127<h4>postThumbTemplate.html</h4>
128
129<p>This is the file where is placed the html box for each post that will be displayed in the main page.</p>
130
131<pre><code class="language-html"><div class="well">
132 [blogo-index-post-template]
133</div>
134</code></pre>
135
136<h4>post01thumb.md</h4>
137
138<pre><code class="language-markdown"># Post 01 thumb
139This is the description of the Post 01
140</code></pre>
141
142<h4>post01.md</h4>
143
144<pre><code class="language-markdown"># Title of the Post 01
145Hi, this is the content of the post
146
147'''js
148 console.log("hello world");
149'''
150</code></pre>
151
152<h2>Let’s start to code</h2>
153
154<p>As we have exposed, we want to develop a Go lang script that from some HTML template and the Markdown text files, generates the complete blog with the main page and all the posts.</p>
155
156<h4>readConfig.go</h4>
157
158<p>This is the file that reads the <em>blogo.json</em> file to get the blog configuration.</p>
159
160<pre><code class="language-go">package main
161
162import (
163 "encoding/json"
164 "io/ioutil"
165)
166
167//Post is the struct for each post of the blog
168type Post struct {
169 Thumb string `json:"thumb"`
170 Md string `json:"md"`
171}
172
173//Config gets the config.json file into struct
174type Config struct {
175 Title string `json:"title"`
176 IndexTemplate string `json:"indexTemplate"`
177 PostThumbTemplate string `json:"postThumbTemplate"`
178 Posts []Post `json:"posts"`
179 CopyRaw []string `json:"copyRaw"`
180}
181
182var config Config
183
184func readConfig(path string) {
185 file, err := ioutil.ReadFile(path)
186 check(err)
187 content := string(file)
188 json.Unmarshal([]byte(content), &config)
189}
190</code></pre>
191
192<h4>files.go, the operations with files</h4>
193
194<p>We will need some file operation functions, so we have placed all in this file.</p>
195
196<pre><code class="language-go">package main
197
198import (
199 "io/ioutil"
200 "os/exec"
201 "strings"
202
203 "github.com/fatih/color"
204)
205
206func readFile(path string) string {
207 dat, err := ioutil.ReadFile(path)
208 if err != nil {
209 color.Red(path)
210 }
211 check(err)
212 return string(dat)
213}
214
215func writeFile(path string, newContent string) {
216 err := ioutil.WriteFile(path, []byte(newContent), 0644)
217 check(err)
218 color.Green(path)
219}
220
221func getLines(text string) []string {
222 lines := strings.Split(text, "\n")
223 return lines
224}
225
226func concatStringsWithJumps(lines []string) string {
227 var r string
228 for _, l := range lines {
229 r = r + l + "\n"
230 }
231 return r
232}
233
234func copyRaw(original string, destination string) {
235 color.Green(original + " --> to --> " + destination)
236 _, err := exec.Command("cp", "-rf", original, destination).Output()
237 check(err)
238}
239</code></pre>
240
241<h4>main.go</h4>
242
243<p>To convert the HTML content to Markdown content, we will use <a href="https://github.com/russross/blackfriday">https://github.com/russross/blackfriday</a></p>
244
245<pre><code class="language-go">package main
246
247import (
248 "fmt"
249 "strings"
250
251 blackfriday "gopkg.in/russross/blackfriday.v2"
252)
253
254const directory = "blogo-input"
255
256func main() {
257 readConfig(directory + "/blogo.json")
258 fmt.Println(config)
259
260 // generate index page
261 indexTemplate := readFile(directory + "/" + config.IndexTemplate)
262 indexPostTemplate := readFile(directory + "/" + config.PostThumbTemplate)
263 var blogoIndex string
264 blogoIndex = ""
265 for _, post := range config.Posts {
266 mdpostthumb := readFile(directory + "/" + post.Thumb)
267 htmlpostthumb := string(blackfriday.Run([]byte(mdpostthumb)))
268
269 //put the htmlpostthumb in the blogo-index-post-template
270 m := make(map[string]string)
271 m["[blogo-index-post-template]"] = htmlpostthumb
272 r := putHTMLToTemplate(indexPostTemplate, m)
273 filename := strings.Split(post.Md, ".")[0]
274 r = "<a href='" + filename + ".html'>" + r + "</a>"
275 blogoIndex = blogoIndex + r
276 }
277 //put the blogoIndex in the index.html
278 m := make(map[string]string)
279 m["[blogo-title]"] = config.Title
280 m["[blogo-content]"] = blogoIndex
281 r := putHTMLToTemplate(indexTemplate, m)
282 writeFile("index.html", r)
283
284 // generate posts pages
285 for _, post := range config.Posts {
286 mdcontent := readFile(directory + "/" + post.Md)
287 htmlcontent := string(blackfriday.Run([]byte(mdcontent)))
288
289 m := make(map[string]string)
290 m["[blogo-title]"] = config.Title
291 m["[blogo-content]"] = htmlcontent
292
293 r := putHTMLToTemplate(indexTemplate, m)
294 //fmt.Println(r)
295
296 filename := strings.Split(post.Md, ".")[0]
297 writeFile(filename+".html", r)
298 }
299
300 //copy raw
301 fmt.Println("copying raw:")
302 for _, dir := range config.CopyRaw {
303 copyRaw(directory+"/"+dir, ".")
304 }
305}
306
307func putHTMLToTemplate(template string, m map[string]string) string {
308 lines := getLines(template)
309 var resultL []string
310 for _, line := range lines {
311 inserted := false
312 for k, v := range m {
313 if strings.Contains(line, k) {
314 //in the line, change [tag] with the content
315 lineReplaced := strings.Replace(line, k, v, -1)
316 resultL = append(resultL, lineReplaced)
317 inserted = true
318 }
319 }
320 if inserted == false {
321 resultL = append(resultL, line)
322 }
323 }
324 result := concatStringsWithJumps(resultL)
325 return result
326}
327</code></pre>
328
329<h2>Try it</h2>
330
331<p>To try it, we need to compile the Go code:</p>
332
333<pre><code>> go build
334</code></pre>
335
336<p>And run it:</p>
337
338<pre><code>> ./blogo
339</code></pre>
340
341<p>Or we can just build and run to test:</p>
342
343<pre><code>> go run *.go
344</code></pre>
345
346<p>As the output, we will obtain the html pages with the content:</p>
347
348<ul>
349<li>index.html</li>
350</ul>
351
352<pre><code class="language-html"><!DOCTYPE html>
353<html>
354<head>
355 <title>my blog</title>
356</head>
357<body>
358 <div class="row">
359 <a href='post01.html'>
360 <div class="col-md-3">
361 <h1>Post 01 thumb</h1>
362 <p>This is the description of the Post 01</p>
363 </div>
364 </a>
365 <a href='post02.html'>
366 <div class="col-md-3">
367 <p>Post 02 thumb</p>
368 </div>
369 </a>
370 </div>
371</body>
372</html>
373</code></pre>
374
375<ul>
376<li>post01.html</li>
377</ul>
378
379<pre><code class="language-html"><!DOCTYPE html>
380<html>
381<head>
382 <title>my blog</title>
383</head>
384<body>
385 <div>
386 <h1>Title of the Post 01</h1>
387 <p>Hi, this is the content of the post</p>
388 <pre>
389 <code class="language-js"> console.log(&quot;hello world&quot;);
390 </code>
391 </pre>
392 </div>
393</body>
394</html>
395</code></pre>
396
397<h2>Conclusion</h2>
398
399<p>In this post, we have seen how to develop a very minimalistic static blog template engine with Go. In fact, is the blog engine that I’m using for this blog.</p>
400
401<p>There are lots of blog template engines nowadays, but maybe we don’t need sophisticated engine, and we just need a minimalistic one. In that case, we have seen how to develop one.</p>
402
403<p>The complete project code is able in <a href="https://github.com/arnaucube/blogo">https://github.com/arnaucube/blogo</a></p>
404
405 </div>
406
407 <footer style="text-align:center; margin-top:100px;margin-bottom:50px;">
408 <div class="container">
409 <div class="row">
410 <ul class="list-inline">
411 <li><a href="https://twitter.com/arnaucube"
412 style="color:gray;text-decoration:none;"
413 target="_blank">twitter.com/arnaucube</a>
414 </li>
415 <li><a href="https://github.com/arnaucube"
416 style="color:gray;text-decoration:none;"
417 target="_blank">github.com/arnaucube</a>
418 </li>
419 </ul>
420 </div>
421 <div class="row" style="display:inline-block;">
422 Blog made with <a href="http://github.com/arnaucube/blogo/"
423 target="_blank" style="color: gray;text-decoration:none;">Blogo</a>
424 </div>
425 </div>
426 </footer>
427
428 <script>
429 </script>
430 <script src="js/external-links.js"></script>
431 <script>hljs.initHighlightingOnLoad();</script>
432 <script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.js" integrity="sha384-YNHdsYkH6gMx9y3mRkmcJ2mFUjTd0qNQQvY9VYZgQd7DcN7env35GzlmFaZ23JGp" crossorigin="anonymous"></script>
433 <script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/contrib/auto-render.min.js" integrity="sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl" crossorigin="anonymous"></script>
434 <script>
435 document.addEventListener("DOMContentLoaded", function() {
436 renderMathInElement(document.body, {
437 displayMode: false,
438 // customised options
439 // • auto-render specific keys, e.g.:
440 delimiters: [
441 {left: '$$', right: '$$', display: true},
442 {left: '$', right: '$', display: false},
443 ],
444 // • rendering keys, e.g.:
445 throwOnError : true
446 });
447 });
448 </script>
449 <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
450
451
452 </body>
453</html>
454