package main import ( "bytes" "fmt" "math" "os" "os/exec" "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() { if !isExist("./dist") { if err := mkdir("./dist"); err != nil { sfault("create dist directory failed: %v", err) } } if err := exec.Command("rm", "-rf", "./dist/*").Run(); err != nil { sfault("clear 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() { themePath := fmt.Sprintf("./themes/%s/", o.Config.Theme) destPath := "./dist/" cmd := exec.Command("cp", "-r", themePath+"css", destPath) if err := cmd.Run(); err != nil { sfault("copy theme css source failed: %v", err) } cmd = exec.Command("cp", "-r", themePath+"js", destPath) if err := cmd.Run(); err != nil { sfault("copy theme js source failed: %v", err) } cmd = exec.Command("cp", "-r", themePath+"images", destPath) if err := cmd.Run(); err != nil { 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 }