Skip to content

Latest commit

 

History

History
738 lines (688 loc) · 25.1 KB

README.org

File metadata and controls

738 lines (688 loc) · 25.1 KB

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 即可覆盖本地默认配置