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 <head> <title>Project</title> <meta name="categories" content="Snow/Templates" /> <meta name="tags" content="linux,snow" /> <meta name="date" content="2015-12-22" /> </head> #+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) 可以生成 *rss* ,*atom* 或者其它任意格式(需要自定义模版) **** 配置 #+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) *** 安装 *** 开发 **** 主题目录结构 其中 *templates* 和 *static* 名称不可修改 #+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 <shortcode _name="encrypt" password="1234567"> hello *markdown* </shortcode> <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" %} <link rel="stylesheet" href="{{ config.site.url }}/{{ asset_url }}"> {% 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= 即可覆盖本地默认配置
Documentation ¶
There is no documentation for this package.
Click to show internal directories.
Click to hide internal directories.