静态博客生成器
──╼ ./snow init Welcome to snow 0.1.0. > Where do you want to create your new web site? [.] mysnow > What will be the title of this web site? [snow] > Who will be the author of this web site? The input is required > Who will be the author of this web site? honmaple > What is your URL prefix? (no trailing slash) [http://127.0.0.1:8000] > Do you want to create first page? [Y/n]
└──╼ cd mysnow └──╼ ../snow server -D DEBU Copying @theme/static/css/main.css to output/static/css/main.css INFO Done: Static Processed 1 static files in 588.705µs DEBU Writing output/categories/index.html DEBU Writing output/authors/index.html DEBU Writing output/tags/index.html DEBU Writing output/posts/index.html DEBU Writing output/authors/snow/index.html DEBU Writing output/tags/snow/index.html DEBU Writing output/categories/linux/index.html DEBU Writing output/tags/linux/index.html DEBU Writing output/tags/emacs/index.html DEBU Writing output/categories/linux/emacs/index.html INFO Done: Page Processed 1 normal pages, 0 hidden pages, 0 section pages in 10.087804ms INFO Done: Section Processed 1 posts in 10.1831ms INFO Done: Taxonomy Processed 1 authors, 3 tags, 1 categories in 10.18788ms
└──╼ go install https://github.com/honmaple/snow
└──╼ git clone https://github.com/honmaple/snow --depth=1 └──╼ cd snow └──╼ go mod tidy └──╼ go build .
└──╼ ./snow --help NAME: snow - snow is a static site generator. USAGE: snow [global options] command [command options] [arguments...] VERSION: 0.1.0 COMMANDS: init init a new site build build and output server server local files help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --config FILE, -c FILE load configuration from FILE (default: "config.yaml") --help, -h show help (default: false) --version, -v print the version (default: false)
└──╼ ./snow init └──╼ ./snow init myblog
如果不指定 myblog 目录,默认会在当前目录下生成一个 config.yaml 文件和一个 content 目录
该命令会构建站点内容内写入到 {output_dir} 目录, 如果该目录已经有文件存在,除非制定 -C 参数,否则不会自动清理
- 清理输出目录
└──╼ ./snow build --clean └──╼ ./snow build -C - 显示输出详情
└──╼ ./snow build --debug └──╼ ./snow build -D - 指定输出目录
└──╼ ./snow build --output {output_dir} └──╼ ./snow build -o {output_dir} - 指定mode
└──╼ ./snow build --mode {mode} └──╼ ./snow build -m {mode}
build 支持的命令 server也同样支持, 除此之外,还有
- 指定监听地址
└──╼ ./snow server --listen 127.0.0.1:8088 └──╼ ./snow server -l 127.0.0.1:8088默认监听地址是
site.url - 监听文件修改并重新构建
└──╼ ./snow server --autoload └──╼ ./snow server -r
显示所有hooks
└──╼ ./snow hooks assets(enabled), encrypt(enabled), filter, minify, pelican(enabled), rewrite(enabled), shortcode(enabled)
. ├── config.yaml ├── content │ └── posts │ └── first-page.md ├── static ├── templates └── themes │ └── snow │ └── static │ └── templates
- config.yaml: 配置文件
- content: 内容目录
- static: 静态文件目录,该目录下的所有文件都会同步到站点的根目录
- templates: 模版目录
- themes: 主题目录
# 站点配置信息
base_url: "http://127.0.0.1:8000"
title: "Snow"
description: "snow is a static site generator."
author: "honmaple"
language: "zh"
# 发布时使用的配置
modes.publish:
base_url: "https://honmaple.me"
# 多语言en使用的配置
languages.en:
base_url: "https://honmaple.me/en"
output_dir: "output"
content_dir: "content"
theme: "snow"
# 按照主题需要进行配置
params.extra:
menus:
- name: "关于"
url: "/pages/about.html"# 指定内容目录
content_dir: "content"
# 忽略的内容,如果是目录则以 / 结尾
ignored_content:
- "drafts/"content_dir 目录下以 _index. 开头的文件都将识别为 section 结构,=secion= 下包括多篇文章
content/
├── _index.md // <- http://127.0.0.1:8000/index.html,包括content下所有默认语言的文章
├── _index.en.md // <- http://127.0.0.1:8000/en/index.html,包括content下所有语言为en的文章
└── posts // no url
├── post1.org // <- http://127.0.0.1:8000/posts/2022/02/post1.html
└── subposts //
└── _index.org // <- http://127.0.0.1:8000/posts/subposts/index.html
└── post2.org // <- http://127.0.0.1:8000/posts/2023/02/post2.html
sections:
_default:
# 页面默认排序, 多字段使用逗号分隔
sort_by: "date desc"
# 自定义某个section下的页面筛选
filter_by: ""
# 页面默认分页, path必须使用{number}变量, 0表示不分页
paginate: 10
# 分页路径
paginate_path: "{name}{number}{extension}"
# 分页前筛选pages
paginate_filter: ""
# 生成路径, 为空表示禁止生成相关页面
path: "{section}/index.html"
# 使用的模版
template: "section.html"
# section输出格式
formats.atom:
path: "{section:slug}/atom.xml"
en:
# en目录下的所有section都是en语言
lang: "en"filter_by 格式(下同):
'emacs' in tags and not draft or weight > 1
其中 tags, draft 等都是 FrontMatter
| 变量 | 描述 |
|---|---|
| {lang} | section所属language |
| {lang:optional} | section所属language,如果是默认语言则为空 |
| {path} | section所属路径 |
| {path:slug} | section所属路径, 转为slug |
| {section} | section名称 |
| {section:slug} | section slug, 中国 -> zhong-guo |
| 变量 | 描述 |
|---|---|
| section | |
| section.Lang | section语言 |
| section.Slug | |
| section.Title | section标题 |
| section.Path | section相对链接 |
| section.Permalink | section绝对链接 |
| section.Content | section内容 |
| section.RawContent | section原始内容 |
| section.Pages | 当前section下的页面列表 |
| section.Children | 子section |
| section.Formats | section输出的其它格式 |
- markdown
--- title: "title" categories: - Snow/Templates tags: - linux - snow --- - orgmode
#+TITLE: title #+DATE: 2022-02-26 17:14:46 #+CATEGORIES: Snow/Templates #+PROPERTY: TAGS linux,snow #+PROPERTY: MODIFIED 2023-02-26 14:35:37 - html
<head> <title>Project</title> <meta name="categories" content="Snow/Templates" /> <meta name="tags" content="linux,snow" /> <meta name="date" content="2015-12-22" /> </head>
可以在配置文件设置页面的 =FrontMatter=,如果页面已有则忽略
pages:
_default:
comment: true
posts:
path: "/articles/{date:%Y}/{date:%m}/{slug}.html"
template: "post.html"
comment: true
pages:
hidden: true
path: "/pages/{slug}.html"
template: "page.html"
drafts:
draft: true
en:
lang: "en"
path: "/en/articles/{date:%Y}/{date:%m}/{slug}.html"
template: "post.html"
en/pages:
hidden: true
path: "/en/pages/{slug}.html"
template: "page.html"| 变量 | 描述 |
|---|---|
| {lang} | 页面语言 |
| {lang:optional} | 页面所属language,如果是默认语言则为空 |
| {date:%Y} | 创建页面的年份 |
| {date:%m} | 创建页面的月份 |
| {date:%d} | 创建页面的日期 |
| {date:%H} | 创建页面的小时 |
| {path} | 页面所属路径 |
| {path:slug} | 页面所属路径, 转为slug |
| {slug} | 页面标题或自定义slug |
| {title} | 页面标题 |
| 变量 | 描述 |
|---|---|
| page | |
| page.Title | 页面标题 |
| page.Lang | 页面语言 |
| page.Date | 页面创建时间 |
| page.Modified | 页面修改时间 |
| page.Aliases | 页面其它链接 |
| page.Path | 页面相对链接 |
| page.Permalink | 页面绝对链接 |
| page.Summary | 页面简介 |
| page.Content | 页面内容 |
| page.FrontMatter.xxx | 自定义的元数据 |
taxonomies:
_default:
# terms排序, 可选name,count
sort_by: ""
path: "{taxonomy}/index.html"
template: "{taxonomy}/list.html"
term_path: "{taxonomy}/{term:slug}/index.html"
term_template: "{taxonomy}/single.html"
# 页面列表筛选
term_filter_by: ""
# 页面列表排序
term_sort_by: "date desc"
# 页面列表分页
term_paginate: 0
term_paginate_path: ""
term_paginate_filter: ""
categories:
authors:
tags:- taxonomies.xxx.path
变量 描述 {lang} 分类所属语言 {lang:optional} 分类所属语言,如果是默认语言则为空 {taxonomy} 分类系统名称 - taxonomies.xxx.term_path
变量 描述 {lang} 分类所属语言 {lang:optional} 分类所属语言,如果是默认语言则为空 {taxonomy} 分类系统名称 {term} 分类具体名称 {term:slug} 分类slug
- taxonomies.xxx.template
变量 描述 taxonomy taxonomy.Name 分类系统名称, 如:categories,tags,authors taxonomy.Terms - taxonomies.xxx.term_template
变量 描述 term term.Name 分类名称 term.Path 相对链接 term.Permalink 绝对链接 term.List 页面列表 term.Children 子分类
snow 中的分类系统是基于归档实现的,该功能类似 SQL 中的 group by, 所以如果要实现归档页可以有两种方式:
- 添加
taxonomies.{key},{key}可以是页面元数据里的任意字段, 比如categories,tags, 如果需要按照时间归档, 格式为date:2006/01, 其中2006/01为Go时间格式,表示按年月归档, 并生成链接 /archives/2022/10/index.htmltaxonomies: date:2006/01: path: "archives/index.html" template: "archives.html" term_path: "archives/{term}/index.html" term_template: "period_archives.html"
- 在
{content_dir}下添加一个archives.md的文件path: archives.html template: archives.html section: true然后在模板
{templates}/archives.html使用pages.GroupBy({key}){%- for subterm in pages.GroupBy("date:2006-01").OrderBy("name desc") %} {%- set date = subterm.Name | split:"-" %} {%- set year = date[0] %} {%- set month = date[1] %} ... {%- endfor %}
| 变量 | 描述 |
|---|---|
| {name} | 路径名称 |
| {extension} | 路径扩展 |
| {number} | 页码, 第一页为”1” |
| {number:optional} | 页码, 第一页为空 |
- 示例一:
path: "section/index.html" paginate_path: "{name}{number:optional}{extension}"
- 第一页:
section/index.html - 第二页:
section/index2.html - 第三页:
section/index3.html
- 第一页:
- 示例二:
path: "section/index.html" paginate_path: "page/{number}{extension}"
- 第一页:
section/page/1.html - 第二页:
section/page/2.html - 第三页:
section/page/3.html
- 第一页:
| 变量 | 描述 |
|---|---|
| paginator | |
| paginator.Path | 分页链接 |
| paginator.PageNum | 当前页 |
| paginator.Total | 总页数 |
| paginator.HasPrev() | 是否有上一页 |
| paginator.Prev | 上一页 |
| paginator.Prev.URL | 上一页链接 |
| paginator.HasNext() | 是否有下一页 |
| paginator.Next | 下一页 |
| paginator.Next.URL | 下一页链接 |
| paginator.All | 所有页 |
| paginator.List | 当前分页下的页面列表 |
页面中的 FrontMatter 中的 draft: true 将会识别为草稿,如果不想每一篇草稿都添加相应字段,可以添加 pages 配置
pages:
drafts:
draft: true配置表示 {content_dir}/drafts 该目录下的所有页面都将被识别为草稿。
构建时忽略草稿
- 忽略草稿目录
ignored_content: - "drafts/"
- 使用命令行参数
snow build --include-drafts
可以生成 rss ,*atom* 或者其它任意格式(需要自定义模版)
# 设置rss格式的默认值
formats.rss:
template: "rss.xml"
formats.atom:
template: "atom.xml"
sections:
_default:
# rss生成路径, 模版将会使用默认模版
formats.rss.path: "{section:slug}/index.xml"
# 为空时禁止生成
formats.atom.path: ""
taxonomies:
tags:
formats.atom:
path: "tags/{term:slug}/index.xml"
# 自定义模版
template: "custom.atom.xml"| 变量 | 描述 |
|---|---|
| section | 仅生成section 有效 |
| term | 仅生成taxonomy term 有效 |
| pages | 页面列表 |
静态文件分 当前目录下静态文件 和 主题静态文件
# 忽略的静态文件,如果是目录则以 / 结尾
ignored_static:
- "extra/"当 static/{filename} 和 themes/{theme}/static/{filename} 同时存在时,优先当前目录下的文件
需要配置 languages
languages.en:
translations: "i18n/en.yaml"
taxonomies:
special_tags:
path: "{taxonomy}/index.html"
languages.fr:
translations: "i18n/fr.yaml"页面格式:
{title}.en.md{title}.fr.md
或者可以在文件头指定 lang: en
- 模版
{% i18n "tags" %} {% T "tags %d" 12 %} {{ i18n("authors") }} {{ T("authors") }} {{ _("authors %f", 3.14) }}甚至可以直接使用变量 {{ _(term.Name) }}
- 翻译文件
默认会加载主题下 i18n 目录下的文件
i18n ├── en.yaml └── zh.yaml文件内容
--- - id: "authors" tr: "作者" - id: "tags" tr: "标签"
也可以自定义文件位置或翻译内容覆盖主题原有的翻译
languages.en: translations: "i18n/en.yaml" languages.zh: translations: - id: "authors" tr: "作者"
https://github.com/flosch/pongo2
其中 templates 和 static 名称不可修改
simple/ ├── theme.yaml ├── templates │ ├── post.html │ ├── index.html │ ├── archives.html ├── static │ ├── main.css
theme:
# 主题名称, 未设置将使用默认主题
name: "test-theme"
# 默认的主题配置,该配置会自动合并,除非设置为空
config: "theme.yaml"
# 主题模版覆盖, 增加同名的文件到 *override* 配置的目录, snow将会优先使用该文件
override: "layouts"重写文章的 FrontMatter 字段,不用修改原文章的内容
hooks.rewrite:
enabled: true配置选项,如果设置 type 为 list=,将会把原字段以 =逗号 分隔进行转换
hooks.rewrite:
option:
- src: "tag"
dst: "tags"
type: "list"文章内容加密
hooks.encrypt:
enabled: true配置选项
hooks.encrypt:
option:
password: "默认密码"
description: "默认描述"- 添加密码到
frontmatter--- password: "123456" ---
- 局部加密:可以使用
shortcodes中的encrypt功能<shortcode encrypt password="123456"> 加密的内容 </shortcode>
{{ page.Content | encrypt:"123456" }}用于快速插入已有模版
hooks.shortcode:
enabled: true在 templates 目录下添加 shortcodes/bilibili.html
<div class="shortcodes-bilibili">
<iframe
src="https://player.bilibili.com/player.html?bvid={{ params.id }}&page={% if params.page %}{{ params.page }}{% else %}1&high_quality=1&danmaku=0&as_wide=0{% endif %}&autoplay=0"
scrolling="no"
border="0"
frameborder="no"
framespacing="0"
allowfullscreen="true"
>
</iframe>
</div>模版内参数
name: 当前shortcode的名称params.*: 传递的参数,比如params.idbodycounter: 统计shortcode在当前文章中的使用次数
<bilibili id="BV1yB4cz8E9y" />或者
<shortcode bilibili id="BV1yB4cz8E9y" />静态文件处理,区别于 static 目录, assets 目录支持合并,压缩等功能
mysite/ ├── assets │ ├── js │ │ ├── main.js │ │ └── theme.js │ ├── scss │ │ ├── _variables.scss │ │ ├── entry.scss │ │ ├── prose.scss │ │ ├── style.scss │ │ ├── theme.scss │ ├── css │ │ ├── custom.css
hooks.assets:
enabled: true配置选项
hooks.assets:
...
option.css:
files:
- "scss/style.scss"
- "css/custom.css"
filters:
- "cssmin"
output: "static/lib.min.css"如果文件以 .scss 或者 .sass=,将自动编译为 =.css
- 使用配置的名称
css{% assets css %} <link rel="stylesheet" href="{{ config.base_url }}/{{ asset_url }}"> {% endassets %} - 在模版中调用
assets{% assets files="css/style.scss" filters="cssmin" output="css/style.min.css" %} <link rel="stylesheet" href="{{ config.base_url }}/{{ asset_url }}"> {% endassets %}
minify 允许对写入的=html=、=css=、=js=文件进行压缩
hooks.minify:
enabled: true配置选项
hooks.minify:
option:
js: true
css: true
html: truesnow 提供了 mode 配置用于区分本地测试和正式发布
base_url: "http://127.0.0.1:8000"
modes.publish:
base_url: "https://example.com"
modes.develop:
include: "develop.yaml"只要在构建时使用 snow build --mode publish 即可覆盖本地默认配置