423 lines
10 KiB
Go
423 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"sort"
|
|
"text/template"
|
|
)
|
|
|
|
type Outter struct {
|
|
MetaData map[string]interface{} // 记录source/data下的所有文件
|
|
Posts []Post // 记录文章
|
|
Pages []Page // 记录页面
|
|
Theme Theme // 记录主题模板文件
|
|
Config Config // 全局配置
|
|
Paginate bool // 是否开启了分页
|
|
PageTotal int // 页面总数
|
|
PageCurrent int // 当前分页
|
|
}
|
|
|
|
type PostOutter struct {
|
|
Post Post
|
|
Config Config
|
|
}
|
|
|
|
type PageOutter struct {
|
|
Page Page
|
|
Config Config
|
|
PostData []PostData
|
|
}
|
|
|
|
type PostData struct {
|
|
Key string
|
|
Posts []Post
|
|
}
|
|
|
|
// check 检测目标
|
|
func (o *Outter) check() {
|
|
var args []string
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
args = []string{"cmd.exe", "/C", "rmdir", "/S", "/Q", "dist"}
|
|
default:
|
|
args = []string{"rm", "-rf", "./dist"}
|
|
}
|
|
|
|
var cmd = exec.Command(args[0], args[1:]...)
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
sout(cmd.String())
|
|
serr("clear dist directory failed: %v", err)
|
|
}
|
|
|
|
if !isExist("./dist") {
|
|
if err := mkdir("./dist"); err != nil {
|
|
sfault("create dist directory failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (o *Outter) createDir(fp string) error {
|
|
if !isExist(fp) {
|
|
if err := mkdir(fp); err != nil {
|
|
sfault("create %s directory failed: %v", fp, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// sourceCopy 资源拷贝 将主题的资源拷贝到目标文件夹中
|
|
func (o *Outter) sourceCopy() {
|
|
var args []string
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
args = []string{"cmd.exe", "/C", "xcopy", "/e"}
|
|
default:
|
|
args = []string{"cp", "-r"}
|
|
}
|
|
themePath := slash(fmt.Sprintf("./themes/%s/", o.Config.Theme))
|
|
destPath := slash("./dist/")
|
|
cmd := exec.Command(args[0], append(args, themePath+"css", destPath)...)
|
|
if err := cmd.Run(); err != nil {
|
|
sout(cmd.String())
|
|
sfault("copy theme css source failed: %v", err)
|
|
}
|
|
cmd = exec.Command(args[0], append(args, themePath+"js", destPath)...)
|
|
if err := cmd.Run(); err != nil {
|
|
sout(cmd.String())
|
|
sfault("copy theme js source failed: %v", err)
|
|
}
|
|
cmd = exec.Command(args[0], append(args, themePath+"images", destPath)...)
|
|
if err := cmd.Run(); err != nil {
|
|
sout(cmd.String())
|
|
sfault("copy theme images source failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// generate 文件生成
|
|
func (o *Outter) generate() {
|
|
o.check()
|
|
o.sourceCopy()
|
|
o.generateIndex()
|
|
o.generatePost()
|
|
o.generatePage()
|
|
o.generateArchives()
|
|
o.generateTags()
|
|
}
|
|
|
|
var funcMap = template.FuncMap{
|
|
"getSource": getSource,
|
|
"add": add,
|
|
"sum": sum,
|
|
}
|
|
|
|
// generateIndex 首页生成
|
|
func (o *Outter) generateIndex() {
|
|
indexTemplate := template.New("index")
|
|
|
|
funcMap["title"] = func() string {
|
|
return o.Config.Title
|
|
}
|
|
|
|
indexTemplate.Funcs(funcMap)
|
|
|
|
indexTemplate, err := indexTemplate.Parse(string(o.Theme.IndexLayout))
|
|
if err != nil {
|
|
sfault("parse index layout failed: %v", err)
|
|
}
|
|
|
|
// 首页文件
|
|
var buffer = bytes.Buffer{}
|
|
|
|
// 没开分页
|
|
if !o.Config.Site.Paginate {
|
|
err = indexTemplate.Execute(&buffer, o)
|
|
if err != nil {
|
|
sfault("generate index page failed: %v", err)
|
|
}
|
|
var filename = "./dist/index.html"
|
|
if err := os.WriteFile(filename, buffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write index file failed: %v", err)
|
|
}
|
|
sout("index generate success...")
|
|
return
|
|
}
|
|
|
|
// 分页数据不规范
|
|
if o.Config.Site.PageSize < 1 {
|
|
sfault("page size must > 1")
|
|
}
|
|
|
|
var pageSize = int(math.Ceil(float64(len(o.Posts)) / float64(o.Config.Site.PageSize)))
|
|
o.PageTotal = pageSize
|
|
o.Paginate = true
|
|
var posts = make([]Post, len(o.Posts))
|
|
copy(posts, o.Posts)
|
|
for i := 0; i < pageSize; i++ {
|
|
rightBorder := int(o.Config.Site.PageSize) * (i + 1)
|
|
if rightBorder > len(posts) {
|
|
rightBorder = len(posts)
|
|
}
|
|
leftBorder := int(o.Config.Site.PageSize) * (i)
|
|
o.Posts = make([]Post, rightBorder-leftBorder)
|
|
copy(o.Posts, posts[leftBorder:rightBorder])
|
|
o.PageCurrent = i + 1
|
|
|
|
err = indexTemplate.Execute(&buffer, o)
|
|
if err != nil {
|
|
sfault("generate index page failed: %v", err)
|
|
}
|
|
// 第一页 主页
|
|
if i == 0 {
|
|
// 第一次的时候创建目录
|
|
dir := "./dist/page"
|
|
if err := o.createDir(dir); err != nil {
|
|
sfault("create index page failed: %v", err)
|
|
return
|
|
}
|
|
var filename = "./dist/index.html"
|
|
if err := os.WriteFile(filename, buffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write index file failed: %v", err)
|
|
}
|
|
}
|
|
fPage := fmt.Sprintf("./dist/page/%d.html", i+1)
|
|
if err := os.WriteFile(fPage, buffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write index file failed: %v", err)
|
|
}
|
|
buffer.Reset()
|
|
}
|
|
sout("index generate success...")
|
|
o.Posts = make([]Post, len(posts))
|
|
copy(o.Posts, posts)
|
|
}
|
|
|
|
func (o *Outter) generatePost() {
|
|
postTemplate := template.New("post")
|
|
var postBuffer = new(bytes.Buffer)
|
|
for _, post := range o.Posts {
|
|
instance := PostOutter{
|
|
Post: post,
|
|
Config: o.Config,
|
|
}
|
|
funcMap["title"] = func() string {
|
|
return instance.Post.Title
|
|
}
|
|
funcMap["post_name"] = func() string {
|
|
return o.Config.Title
|
|
}
|
|
postTemplate.Funcs(funcMap)
|
|
postTemplate, err := postTemplate.Parse(string(o.Theme.PostLayout))
|
|
if err != nil {
|
|
sfault("generate post page failed: %v", err)
|
|
}
|
|
// 入buffer
|
|
if err := postTemplate.Execute(postBuffer, instance); err != nil {
|
|
sfault("generate post page failed: %v", err)
|
|
}
|
|
// buffer写文件
|
|
var catDir = fmt.Sprintf("./dist/%s", post.Category)
|
|
if !isExist(catDir) {
|
|
if err := mkdir(catDir); err != nil {
|
|
sfault("create directory failed: %v", err)
|
|
}
|
|
}
|
|
var filename = fmt.Sprintf("%s/%s.html", catDir, post.FileBasename)
|
|
if err := os.WriteFile(filename, postBuffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write file failed: %v", err)
|
|
}
|
|
postBuffer.Reset()
|
|
}
|
|
|
|
sout("post generate success...")
|
|
}
|
|
|
|
func (o *Outter) generatePage() {
|
|
pageTemplate := template.New("page")
|
|
var pageBuffer = new(bytes.Buffer)
|
|
for _, page := range o.Pages {
|
|
instance := PageOutter{
|
|
Page: page,
|
|
Config: o.Config,
|
|
}
|
|
funcMap["title"] = func() string {
|
|
return page.Title
|
|
}
|
|
funcMap["page_name"] = func() string {
|
|
return o.Config.Title
|
|
}
|
|
pageTemplate.Funcs(funcMap)
|
|
pageTemplate, err := pageTemplate.Parse(string(o.Theme.PageLayout))
|
|
if err != nil {
|
|
sfault("generate page page failed: %v", err)
|
|
}
|
|
// 入buffer
|
|
if err := pageTemplate.Execute(pageBuffer, instance); err != nil {
|
|
sfault("generate page page failed: %v", err)
|
|
}
|
|
// buffer写文件
|
|
var filename = fmt.Sprintf("./dist/%s.html", page.Link)
|
|
if err := os.WriteFile(filename, pageBuffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write page content failed: %v", err)
|
|
}
|
|
pageBuffer.Reset()
|
|
}
|
|
|
|
sout("page generate success...")
|
|
}
|
|
|
|
func (o *Outter) generateArchives() {
|
|
archiveTemplate := template.New("archive")
|
|
var archiveBuffer = new(bytes.Buffer)
|
|
// 按时间归档
|
|
var m = make(map[string][]Post)
|
|
for _, post := range o.Posts {
|
|
newPost := post
|
|
newPost.MD = ""
|
|
m[int2String(post.CreatedAt.Year())] = append(m[int2String(post.CreatedAt.Year())], newPost)
|
|
}
|
|
|
|
var postData []PostData
|
|
for year, posts := range m {
|
|
postData = append(postData, PostData{
|
|
Key: year,
|
|
Posts: posts,
|
|
})
|
|
}
|
|
|
|
sort.Slice(postData, func(i, j int) bool {
|
|
return postData[i].Key > postData[j].Key
|
|
})
|
|
|
|
var instance = PageOutter{
|
|
Page: Page{
|
|
Title: "Archives",
|
|
Link: "archives",
|
|
},
|
|
Config: o.Config,
|
|
PostData: postData,
|
|
}
|
|
funcMap["title"] = func() string {
|
|
return instance.Page.Title
|
|
}
|
|
funcMap["page_name"] = func() string {
|
|
return o.Config.Title
|
|
}
|
|
archiveTemplate.Funcs(funcMap)
|
|
archiveTemplate, err := archiveTemplate.Parse(string(o.Theme.ArchiveLayout))
|
|
if err != nil {
|
|
sfault("generate archive page failed: %v", err)
|
|
}
|
|
|
|
// 入buffer
|
|
if err := archiveTemplate.Execute(archiveBuffer, instance); err != nil {
|
|
sfault("generate archive page failed: %v", err)
|
|
}
|
|
// buffer写文件
|
|
var filename = fmt.Sprintf("./dist/%s.html", instance.Page.Link)
|
|
if err := os.WriteFile(filename, archiveBuffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write archive page failed: %v", err)
|
|
}
|
|
archiveBuffer.Reset()
|
|
|
|
sout("archives generate success...")
|
|
}
|
|
|
|
func (o *Outter) generateTags() {
|
|
tagsTemplate := template.New("tags")
|
|
var tagsBuffer = new(bytes.Buffer)
|
|
|
|
// 按标签归档
|
|
var mTag = make(map[string][]Post)
|
|
for _, post := range o.Posts {
|
|
for _, tag := range post.Tags {
|
|
newPost := post
|
|
newPost.MD = ""
|
|
mTag[tag] = append(mTag[tag], newPost)
|
|
}
|
|
}
|
|
|
|
for tag, posts := range mTag {
|
|
// 按时间归档
|
|
var m = make(map[string][]Post)
|
|
for _, post := range posts {
|
|
newPost := post
|
|
newPost.MD = ""
|
|
m[int2String(post.CreatedAt.Year())] = append(m[int2String(post.CreatedAt.Year())], newPost)
|
|
}
|
|
|
|
var postData []PostData
|
|
for year, _posts := range m {
|
|
postData = append(postData, PostData{
|
|
Key: year,
|
|
Posts: _posts,
|
|
})
|
|
}
|
|
|
|
sort.Slice(postData, func(i, j int) bool {
|
|
return postData[i].Key > postData[j].Key
|
|
})
|
|
|
|
var instance = PageOutter{
|
|
Page: Page{
|
|
Title: tag,
|
|
Link: tag,
|
|
},
|
|
Config: o.Config,
|
|
PostData: postData,
|
|
}
|
|
|
|
funcMap["title"] = func() string {
|
|
return instance.Page.Title
|
|
}
|
|
funcMap["page_name"] = func() string {
|
|
return o.Config.Title
|
|
}
|
|
tagsTemplate.Funcs(funcMap)
|
|
tagsTemplate, err := tagsTemplate.Parse(string(o.Theme.TagLayout))
|
|
if err != nil {
|
|
sfault("generate tags page failed: %v", err)
|
|
}
|
|
|
|
// 入buffer
|
|
if err := tagsTemplate.Execute(tagsBuffer, instance); err != nil {
|
|
sfault("generate tags page failed: %v", err)
|
|
}
|
|
// 创建tag文件夹
|
|
var tagDir = "./dist/tags"
|
|
if !isExist(tagDir) {
|
|
if err := mkdir(tagDir); err != nil {
|
|
sfault("create tag directory failed: %v", err)
|
|
}
|
|
}
|
|
// buffer写文件
|
|
var filename = fmt.Sprintf("%s/%s.html", tagDir, instance.Page.Link)
|
|
if err := os.WriteFile(filename, tagsBuffer.Bytes(), os.ModePerm); err != nil {
|
|
sfault("write tag content failed: %v", err)
|
|
}
|
|
tagsBuffer.Reset()
|
|
}
|
|
|
|
sout("tags generate success...")
|
|
}
|
|
|
|
func getSource(data interface{}, key string) interface{} {
|
|
meta, ok := data.(map[string]interface{})
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return meta[key]
|
|
}
|
|
|
|
func add(a, b int) int {
|
|
return a + b
|
|
}
|
|
|
|
func sum(a, b int) int {
|
|
return a - b
|
|
}
|