Skip to content

honmaple/snow

Folders and files

NameName
Last commit message
Last commit date
Oct 9, 2024
Oct 9, 2024
Mar 27, 2024
Mar 27, 2024
Jun 28, 2023
Feb 23, 2023
Sep 7, 2019
Mar 27, 2024
Dec 19, 2023
Dec 19, 2023
Oct 18, 2022

Repository files navigation

Snow

静态博客生成器

快速开始

开始(Quickstart)

创建新的站点

──╼ ./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

安装(Installation)

└──╼ go install https://github.com/honmaple/snow

编译(Build)

└──╼ git clone https://github.com/honmaple/snow --depth=1
└──╼ cd snow
└──╼ go mod tidy
└──╼ go build .

命令行(Cli usage)

└──╼ ./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)

init

└──╼ ./snow init
└──╼ ./snow init myblog

如果不指定 myblog 目录,默认会在当前目录下生成一个 config.yaml 文件和一个 content 目录

build

该命令会构建站点内容内写入到 {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}
        
  • 筛选页面
    └──╼ ./snow build --filter {build_filter}
    └──╼ ./snow build -F {build_filter}
        
  • 显示所有hooks
    └──╼ ./snow build --hooks
        

server

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
        

目录结构(Driectory structure)

.
├── config.yaml
├── content
│   └── posts
│       └── first-page.md
├── static
├── layouts
└── themes
│   └── snow
│       └── static
│       └── template
  • config.yaml: 使用的配置文件
  • content: 包括所有的页面内容, 比如 .md, .org 等,如果一个子目录包括 index.{md,org} 文件,那么这个目录将会成为一个页面,否则每一个子目录都是一个 section, 同样的,子目录下 _index.{md,org} 文件也是该 section 的配置文件
  • static: static_dirs 指定的静态文件或目录,名称可修改
  • layouts: 主题模版覆盖目录 theme.override 指定的主题覆盖文件,比如有一个主题模版 {theme}/templates/post.html, 当指定了 override 目录后就可以在该目录创建一个同样名称为 post.html 的文件进行覆盖
  • themes: 主题目录, 该目录下包括的子目录就是主题名称,可以在 theme.name 里指定

配置文件(Configuration)

# 站点配置信息
site:
  url: "http://127.0.0.1:8000"
  title: "snow"
  subtitle: "Snow is a static generator."
  language: "zh"
  author: "honmaple"

# 发布时使用的配置
mode.publish:
  site:
    url: "https://honmaple.me"

output_dir: "output"
content_dir: "content"
build_filter: "not draft"

theme:
  name: "snow"

# 按照主题需要进行配置
params.extra:
  menus:
    - name: "关于"
      url: "/pages/about.html"

内容管理

Section

content/
├── pages             // no url, because sections.pages.path is ""
│   └── about         // <- http://127.0.0.1:8000/pages/about.html
│       └── index.org // no url
│   └── contact.org   // <- http://127.0.0.1:8000/pages/contact.html
└── posts             // <- http://127.0.0.1:8000/posts/index.html
    ├── post1.org     // <- http://127.0.0.1:8000/posts/2022/02/post1.html
    └── subposts      // <- 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:
    # 页面默认排序, 多字段使用逗号分隔
    orderby: "date desc"
    # 自定义某个section下的页面筛选
    filter: ""
    # 页面默认分页, path必须使用{number}变量, 0表示不分页
    paginate: 10
    # 分页路径
    paginate_path: "{name}{number}{extension}"
    # 分页前筛选pages
    paginate_filter: ""
    # 生成路径, 为空表示禁止生成相关页面
    path: "{section}/index.html"
    # 使用的模版
    template: "section.html"
    # 当前section下所有页面生成路径
    page_path: "{section}/{slug}/index.html"
    # 页面使用的模版
    page_template: "post.html"
    formats.atom:
      path: "{section:slug}/atom.xml"
  posts:
    page_path: "posts/{date:%Y}/{date:%m}/{slug}.html"
  pages:
    path: ""
  pages/about:
    # 自定义pages/about下的页面生成路径,同时继承pages.path不会生成所有页面
    page_path: "{slug}/index.html"

filter 格式(下同):

'emacs' in tags and not draft or weight > 1

其中 tags, draft 等都是page元数据

路径变量(sections.xxx.path)

变量描述
{section}section名称
{section:slug}section slug, 中国 -> zhong-guo

模版变量(sections.xxx.template)

变量描述
section
section.Titlesection标题
section.Pathsection相对链接
section.Permalinksection绝对链接
section.Contentsection内容
section.Pages当前section下的页面列表
section.Children子section
section.Parent父section

页面(Page)

元数据

  • 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>
        

配置

# 页面目录所在, 其中该目录下应该包括一系列子目录,这些子目录的名称对应为 *页面的类型*, 比如 *content/drafts/* 目录下的 页面类型为 *drafts*, 当然也可以直接在 页面文件头添加 =type: drafts=
content_dir: "content"

路径变量(sections.xxx.page_path)

变量描述
{date:%Y}创建页面的年份
{date:%m}创建页面的月份
{date:%d}创建页面的日期
{date:%H}创建页面的小时
{lang}页面语言
{slug}页面标题或自定义slug
{filename}文件名称(不带后缀名)

模版变量(sections.xxx.page_template)

变量描述
page
page.Title页面标题
page.Lang页面语言
page.Date页面创建时间
page.Modified页面修改时间
page.Aliases页面其它链接
page.Path页面相对链接
page.Permalink页面绝对链接
page.Summary页面简介
page.Content页面内容
page.Meta.xxx自定义的元数据
page.Prev上一篇
page.Next下一篇
page.HasPrev()是否有上一篇
page.HasNext()是否有下一篇
page.PrevInType同一类型上一篇
page.NextInType同一类型下一篇
page.HasPrevInType()是否有同一类型上一篇
page.HasNextInType()是否有同一类型下一篇

分类系统(Taxonomy)

配置

taxonomies:
  _default:
    path: "{taxonomy}/index.html"
    # terms排序, 可选name,count
    orderby: ""
    template: "{taxonomy}/list.html"
    term_path: "{taxonomy}/{term:slug}/index.html"
    term_template: "{taxonomy}/single.html"
    # 页面列表筛选
    term_filter: ""
    # 页面列表排序
    term_orderby: "date desc"
    # 页面列表分页
    term_paginate: 0
    term_paginate_path: ""
    term_paginate_filter: ""
  categories:
  authors:
  tags:

路径变量

  • taxonomies.xxx.path
    变量描述
    {taxonomy}分类系统名称
  • taxonomies.xxx.term_path
    变量描述
    {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子分类

归档页(Archive)

snow 中的分类系统是基于归档实现的,该功能类似 SQL 中的 group by, 所以如果要实现归档页可以有两种方式:

  1. 添加 taxonomies.{key}, {key} 可以是页面元数据里的任意字段, 比如 categories, tags, 如果需要按照时间归档, 格式为 date:2006/01, 其中 2006/01 为Go时间格式,表示按年月归档, 并生成链接 /archives/2022/10/index.html
    taxonomies:
      date:2006/01:
        path: "archives/index.html"
        template: "archives.html"
        term_path: "archives/{term}/index.html"
        term_template: "period_archives.html"
        
  2. {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 %}
        

分页(Pagination)

路径变量

变量描述
{name}路径名称
{extension}路径扩展
{number}页码, 第一页为空
{number:one}页码, 第一页为”1”
  • 示例一:
    path: "section/index.html"
    paginate_path: "{name}{number}{extension}"
        
    • 第一页: section/index.html
    • 第二页: section/index2.html
    • 第三页: section/index3.html
  • 示例二:
    path: "section/index.html"
    paginate_path: "page/{number:one}{extension}"
        
    • 第一页: section/page/1.html
    • 第二页: section/page/2.html
    • 第三页: section/page/3.html

模版变量

变量描述
paginator
paginator.URL分页链接
paginator.PageNum当前页
paginator.Total总页数
paginator.HasPrev()是否有上一页
paginator.Prev上一页
paginator.Prev.URL上一页链接
paginator.HasNext()是否有下一页
paginator.Next下一页
paginator.Next.URL下一页链接
paginator.All所有页
paginator.List当前分页下的页面列表

草稿(Draft)

使用者可以自定义草稿标志,但推荐使用两种形式:

  1. 添加元数据 draft: true, 构建时增加筛选条件
    • 草稿
      snow build --filter 'draft = true'
              
    • 非草稿
      snow build -F 'not draft'
              
  2. 创建一个单独的 drafts 目录存放草稿
    • 草稿
      snow build -F 'type = "drafts"'
              
    • 非草稿
      snow build -F 'type != "drafts"'
              

注: 默认筛选条件可以写入配置 build_filter

输出格式(Atom,Rss,JSON)

可以生成 rss ,*atom* 或者其它任意格式(需要自定义模版)

配置

# 设置rss格式的默认值
formats.rss:
  template: "_internal/rss.xml"

formats.atom:
  template: "_internal/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页面列表

静态文件(Static)

静态文件分 主题静态文件配置指定的静态文件

主题静态文件

├── themes
│   └── snow
│       └── static
│           └── main.css

主题目录下的所有文件默认会复制到 output 目录, 除非设置 statics.@theme/static.path 为空

指定的静态文件

该文件需要在配置指定

statics:
  # 根目录下static目录下的文件将会拷贝到{output_dir}/static
  static:
    # 拷贝的路径, 为空时表示不写入, 如果以"/"结尾, 表示拷贝到该目录
    # static  -> {output_dir}/static
    # static/ -> {output_dir}/static/static
    path: "/"
    # 指定扩展,不配置将会使用目录下的所有文件
    exts:
      - ".js"
      - ".css"
    # 如果指定的静态文件是一个目录,可以设置忽略文件, 比如忽略static目录下的images子目录
    ignore_files:
      - "^images/"
  # 以@theme/开头表示主题目录, 以@theme/_internal/开头表示内置的主题目录
  @theme/static:
    path: "static"
  @theme/_internal/static:
    path: "static"
  # 同样可以指定任意静态文件或目录
  content/pages/css:
    path: "static/css"

多语言(Multilingual)

需要配置 languages

languages.en:
  translations: "i18n/en.yaml"
  taxonomies:
    special_tags:
      path: "{taxonomy}/index.html"
languages.fr:
  translations: "i18n/fr.yaml"
  ignores:
    # 忽略所有的静态文件,与主站点共用一个静态目录
    - statics

页面格式:

  • {title}.en.md
  • {title}.fr.md

或者可以在文件头指定 lang: en

模版(templates)

https://github.com/flosch/pongo2

主题(theme)

安装

开发

主题目录结构

其中 templatesstatic 名称不可修改

simple/
├── theme.yaml
├── templates
│   ├── post.html
│   ├── index.html
│   ├── archives.html
├── static
│   ├── main.css

配置

theme:
  # 主题名称, 未设置将使用默认主题
  name: "test-theme"
  # 默认的主题配置,该配置会自动合并,除非设置为空
  config: "theme.yaml"
  # 主题模版覆盖, 增加同名的文件到 *override* 配置的目录, snow将会优先使用该文件
  override: "layouts"

插件(hooks)

registered_hooks:
  - "i18n"
  - "assets"
  - "encrypt"
  - "shortcode"

i18n

  • 模版
    {% 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: "作者"
        

encrypt

内容加密, 需要一个密码

{{ page.Content | encrypt:"123456" }}

shortcode

用于快速插入已有模版, 示例:

<shortcode _name="encrypt" password="1234567">
hello *markdown*
</shortcode>

<shortcode _name="gist" author="spf13" id="7896402" />

可以自定义 shortcode 到主题的 templates/shortcodes 目录下, 目前内置 gist, encrypt

  • 如果使用的外部 js,css 文件可以加载内置的 shortcode.js 实现全局只加载一次,具体可以参考 shortcodes/encrypt.html
  • 如果想要在单个页面只加载一次,请使用
    _counter == 0
        

assets

静态文件处理

hooks.assets:
  css:
    files:
      - "@theme/static/scss/main.scss"
      - "@theme/static/scss/entry.scss"
    filters:
      - libscss:
          path: ["@theme/static/scss/"]
      - cssmin:
    output: "static/lib.min.css"
{% assets files="css/style.scss" filters="libsass,cssmin" output="css/style.min.css" %}
<link rel="stylesheet" href="{{ config.site.url }}/{{ asset_url }}">
{% endassets %}

{% assets css %}
<link rel="stylesheet" href="{{ config.site.url }}/{{ asset_url }}">
{% endassets %}

sofile

sofile 允许使用Go的 Plugin 系统支持自定义插件

  • 创建一个 sofile.go 的文件
    package main
    
    import (
        "fmt"
    
        "github.com/honmaple/snow/builder/hook"
        "github.com/honmaple/snow/builder/page"
        "github.com/honmaple/snow/builder/theme"
        "github.com/honmaple/snow/config"
    )
    
    type testHook struct {
        hook.BaseHook
    }
    
    func (testHook) Name() string {
        return "test"
    }
    
    func (testHook) Page(page *page.Page) *page.Page {
        fmt.Println(page.Title)
        return page
    }
    
    func NewHook(conf config.Config, theme theme.Theme) hook.Hook {
        return &testHook{}
    }
        
  • 编译为so文件
    go build -buildmode=plugin sofile.go
        
  • 注册插件
    registered_hooks:
      - "sofile"
    hooks.sofile.files:
      - "sofile.so"
        

本地测试和正式发布

snow 提供了 mode 配置用于区分本地测试和正式发布

site:
  url: "http://127.0.0.1:8000"
  output_dir: "output"

mode.publish:
  site:
    url: "https://example.com"
    output_dir: "xxx"

mode.develop:
  include: "develop.yaml"

只要在构建时使用 snow build --mode publish 即可覆盖本地默认配置