Categorygithub.com/honmaple/snow
modulepackage
0.1.2
Repository: https://github.com/honmaple/snow.git
Documentation: pkg.go.dev

# README

  • Snow 静态博客生成器

** 快速开始 *** 开始(Quickstart) **** 创建新的站点 #+begin_example ──╼ ./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] #+end_example

**** 编译和预览 #+begin_example └──╼ 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 #+end_example

*** 安装(Installation) #+begin_example └──╼ go install https://github.com/honmaple/snow #+end_example

*** 编译(Build) #+begin_example └──╼ git clone https://github.com/honmaple/snow --depth=1 └──╼ cd snow └──╼ go mod tidy └──╼ go build . #+end_example

*** 命令行(Cli usage) #+begin_example └──╼ ./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)
#+end_example

**** init #+begin_example └──╼ ./snow init └──╼ ./snow init myblog #+end_example 如果不指定 myblog 目录,默认会在当前目录下生成一个 config.yaml 文件和一个 content 目录 **** build 该命令会构建站点内容内写入到 {output_dir} 目录, 如果该目录已经有文件存在,除非制定 =-C= 参数,否则不会自动清理 - 清理输出目录 #+begin_example └──╼ ./snow build --clean └──╼ ./snow build -C #+end_example - 显示输出详情 #+begin_example └──╼ ./snow build --debug └──╼ ./snow build -D #+end_example - 指定输出目录 #+begin_example └──╼ ./snow build --output {output_dir} └──╼ ./snow build -o {output_dir} #+end_example - 指定mode #+begin_example └──╼ ./snow build --mode {mode} └──╼ ./snow build -m {mode} #+end_example - 筛选页面 #+begin_example └──╼ ./snow build --filter {build_filter} └──╼ ./snow build -F {build_filter} #+end_example - 显示所有hooks #+begin_example └──╼ ./snow build --hooks #+end_example **** server build 支持的命令 server也同样支持, 除此之外,还有 - 指定监听地址 #+begin_example └──╼ ./snow server --listen 127.0.0.1:8088 └──╼ ./snow server -l 127.0.0.1:8088 #+end_example 默认监听地址是 =site.url= - 监听文件修改并重新构建 #+begin_example └──╼ ./snow server --autoload └──╼ ./snow server -r #+end_example

*** 目录结构(Driectory structure) #+begin_example . ├── config.yaml ├── content │   └── posts │   └── first-page.md ├── static ├── layouts └── themes │   └── snow │   └── static │   └── template #+end_example - 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) #+begin_src yaml # 站点配置信息 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"
#+end_src

** 内容管理 *** Section #+begin_example 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 #+end_example **** 配置 #+begin_src yaml 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" #+end_src filter 格式(下同): #+begin_example 'emacs' in tags and not draft or weight > 1 #+end_example 其中 tags, draft 等都是page元数据

**** 路径变量(sections.xxx.path) |----------------+---------------------------------| | 变量 | 描述 | |----------------+---------------------------------| | {section} | section名称 | | {section:slug} | section slug, 中国 -> zhong-guo |

**** 模版变量(sections.xxx.template) |-------------------+-------------------------| | 变量 | 描述 | |-------------------+-------------------------| | section | | | section.Title | section标题 | | section.Path | section相对链接 | | section.Permalink | section绝对链接 | | section.Content | section内容 | | section.Pages | 当前section下的页面列表 | | section.Children | 子section | | section.Parent | 父section |

*** 页面(Page) **** 元数据 - markdown #+begin_example --- title: "title" categories: - Snow/Templates tags: - linux - snow --- #+end_example - orgmode #+begin_example #+TITLE: title #+DATE: 2022-02-26 17:14:46 #+CATEGORIES: Snow/Templates #+PROPERTY: TAGS linux,snow #+PROPERTY: MODIFIED 2023-02-26 14:35:37 #+end_example - html #+begin_src html Project #+end_src **** 配置 #+begin_src yaml # 页面目录所在, 其中该目录下应该包括一系列子目录,这些子目录的名称对应为 页面的类型, 比如 content/drafts/ 目录下的 页面类型为 drafts, 当然也可以直接在 页面文件头添加 =type: drafts= content_dir: "content" #+end_src **** 路径变量(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) **** 配置 #+begin_src yaml 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: #+end_src

**** 路径变量 - 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 #+begin_src yaml taxonomies: date:2006/01: path: "archives/index.html" template: "archives.html" term_path: "archives/{term}/index.html" term_template: "period_archives.html" #+end_src 2. 在 ={content_dir}= 下添加一个 =archives.md= 的文件 #+begin_example path: archives.html template: archives.html section: true #+end_example 然后在模板 ={templates}/archives.html= 使用 =pages.GroupBy({key})= #+begin_src html {%- 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 %} #+end_src

*** 分页(Pagination) **** 路径变量 |--------------+-------------------| | 变量 | 描述 | |--------------+-------------------| | {name} | 路径名称 | | {extension} | 路径扩展 | | {number} | 页码, 第一页为空 | | {number:one} | 页码, 第一页为"1" | - 示例一: #+begin_src yaml path: "section/index.html" paginate_path: "{name}{number}{extension}" #+end_src - 第一页: =section/index.html= - 第二页: =section/index2.html= - 第三页: =section/index3.html= - 示例二: #+begin_src yaml path: "section/index.html" paginate_path: "page/{number:one}{extension}" #+end_src - 第一页: =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=, 构建时增加筛选条件 - 草稿 #+begin_example snow build --filter 'draft = true' #+end_example - 非草稿 #+begin_example snow build -F 'not draft' #+end_example 2. 创建一个单独的 =drafts= 目录存放草稿 - 草稿 #+begin_example snow build -F 'type = "drafts"' #+end_example - 非草稿 #+begin_example snow build -F 'type != "drafts"' #+end_example

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

*** 输出格式(Atom,Rss,JSON) 可以生成 rssatom 或者其它任意格式(需要自定义模版) **** 配置 #+begin_src yaml # 设置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"
 #+end_src

**** 模版变量 |---------+--------------------------| | 变量 | 描述 | |---------+--------------------------| | section | 仅生成section 有效 | | term | 仅生成taxonomy term 有效 | | pages | 页面列表 |

*** 静态文件(Static) 静态文件分 主题静态文件配置指定的静态文件

**** 主题静态文件 #+begin_example ├── themes │   └── snow │   └── static │   └── main.css #+end_example 主题目录下的所有文件默认会复制到 output 目录, 除非设置 =statics.@theme/static.path= 为空

**** 指定的静态文件 该文件需要在配置指定 #+begin_src yaml 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" #+end_src

*** 多语言(Multilingual) 需要配置 =languages= #+begin_src yaml languages.en: translations: "i18n/en.yaml" taxonomies: special_tags: path: "{taxonomy}/index.html" languages.fr: translations: "i18n/fr.yaml" ignores: # 忽略所有的静态文件,与主站点共用一个静态目录 - statics #+end_src 页面格式: - ={title}.en.md= - ={title}.fr.md= 或者可以在文件头指定 =lang: en=

** 模版(templates) [[https://github.com/flosch/pongo2]] ** 主题(theme) *** 安装 *** 开发 **** 主题目录结构 其中 templatesstatic 名称不可修改 #+begin_example simple/ ├── theme.yaml ├── templates │   ├── post.html │   ├── index.html │   ├── archives.html ├── static │   ├── main.css #+end_example **** 配置 #+begin_src yaml theme: # 主题名称, 未设置将使用默认主题 name: "test-theme" # 默认的主题配置,该配置会自动合并,除非设置为空 config: "theme.yaml" # 主题模版覆盖, 增加同名的文件到 override 配置的目录, snow将会优先使用该文件 override: "layouts" #+end_src

** 插件(hooks) #+begin_src yaml registered_hooks: - "i18n" - "assets" - "encrypt" - "shortcode" #+end_src *** i18n - 模版 #+begin_src html {% i18n "tags" %} {% T "tags %d" 12 %} {{ i18n("authors") }} {{ T("authors") }} {{ _("authors %f", 3.14) }} #+end_src 甚至可以直接使用变量 {{ _(term.Name) }} - 翻译文件 默认会加载主题下 i18n 目录下的文件 #+begin_example i18n ├── en.yaml └── zh.yaml #+end_example 文件内容 #+begin_src yaml --- - id: "authors" tr: "作者" - id: "tags" tr: "标签" #+end_src

   也可以自定义文件位置或翻译内容覆盖主题原有的翻译
   #+begin_src yaml
   languages.en:
     translations: "i18n/en.yaml"
   languages.zh:
     translations:
       - id: "authors"
         tr: "作者"
   #+end_src

*** encrypt 内容加密, 需要一个密码 #+begin_src html {{ page.Content | encrypt:"123456" }} #+end_src

*** shortcode 用于快速插入已有模版, 示例: #+begin_example hello markdown

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

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

 - 如果使用的外部 =js,css= 文件可以加载内置的 =shortcode.js= 实现全局只加载一次,具体可以参考 [[https://github.com/honmaple/snow/blob/master/builder/theme/internal/templates/shortcodes/encrypt.html][shortcodes/encrypt.html]]
 - 如果想要在单个页面只加载一次,请使用
   #+begin_example
   _counter == 0
   #+end_example

*** assets 静态文件处理 #+begin_src yaml 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" #+end_src #+begin_src html {% assets files="css/style.scss" filters="libsass,cssmin" output="css/style.min.css" %} {% endassets %}

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

*** sofile sofile 允许使用Go的 =Plugin= 系统支持自定义插件 - 创建一个 =sofile.go= 的文件 #+begin_src 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{}
  }
  #+end_src
- 编译为so文件
  #+begin_example
  go build -buildmode=plugin sofile.go
  #+end_example
- 注册插件
  #+begin_src yaml
  registered_hooks:
    - "sofile"
  hooks.sofile.files:
    - "sofile.so"
  #+end_src

** 本地测试和正式发布 snow 提供了 mode 配置用于区分本地测试和正式发布 #+begin_src yaml :noindent 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" #+end_src 只要在构建时使用 =snow build --mode publish= 即可覆盖本地默认配置

# Packages

No description provided by the author
No description provided by the author
No description provided by the author