Getting started
← Docs overview简介
什么是 Metalsmith?
Metalsmith 是一个基于 NodeJS 开发的、极简的、插件化的静态网站生成器。 Let us break that down:
Metalsmith 是一款静态网站生成器
静态网站生成器的任务是生成可部署到网络服务器上的静态文件。对于静态网站生成器来说,这意味着:
- 获取源文件所在目录,读取源文件并提取其中的信息
- 使用插件处理信息
- 将处理后的信息写入目标目录的文件中(NPM 是)。
Metalsmith 就是基于这一原理而构建的。即,它从源文件中获取信息,并将处理后的信息写入目标目录的文件中。
Metalsmith 极其简单
- 纯 javascript 对象: 源文件目录一开始被转换成一个 文件(File)对象集合 ,其中,键(key)是文件路径,值(value)是一个包含普通
{key:value}
键值对的对象。这些{key:value}
对包含原始的文件信息(例如mode
或stats
)及content
(也就是正文内容)属性。 每个文件所对应的 File 对象都会添加当前文件起始部分所设置的变量(即 front-matter),或者由插件所设置的变量。 - 一切皆插件: 所有功能完全是由插件实现的。 Metalsmith 自身只提供了一个瀑布式接口,插件通过这个接口完成所有功能。通过更改文件对象的键和值,插件可以方便地完成大部分操作。插件只是一个接收 文件(File)对象 作为参数的函数,其完成自己的功能后调用 done() 函数,— 你猜对了 —,这就代表完成了。任何会编写 javascript 函数的人都能编写 metalsmith 插件。
- 单一依赖: Metalsmith 完全是 纯 javascript 开发的,只需要安装 Node 即可运行。无需安装 Ruby、 Rust 或 Go。无需熟悉 React 或 Vue 这样的抽象层。Start small and add extra plugins over time.
Metalsmith 是插件化的
Metalsmith 将所有操作都留给了插件来实现。
这里所说的操作可以是任何功能:转换模板、转译代码、替换变量、用布局文件包裹内容、文件分组、移动和删除文件等等。这就是为什么我们说 一切皆插件。操作可以一个接一个地执行,或者 链式执行(chained) 或者 管道式执行(piped),随你喜欢。显然,在一个执行链条上,插件的顺序 就变得很重要了。
将 metalsmith 拆分成一个简洁、坚固的核心以及一系列插件,可以降低复杂性。这让你可以自由地使用你所 需要 的插件。并且分散了维护 Metalsmith 生态系统的荣誉和负担。
Metalsmith is more
根据你所面对的使用场景,Metalsmith 可以 不仅仅 是一个静态网站生成器:
- 可以是部署、备份或构建工具
- 可以是文件元数据提取器
- 可以是模板脚手架
- 可以是持续集成管道
- 可以是任何轻量级文件系统抽象层
得益于简单的内存抽象,metalsmith 的构建和插件无需大量模拟就能轻松进行单元测试。 看看 the Metalsmith tests ,你就会有所了解。
前提条件
Metalsmith 可在所有 NodeJS 长期支持版本(生命周期未结束)及其它更多版本上运行。下载并安装 NodeJs 和 NPM(Node 已内置 NPM): https://nodejs.org/en/download/ 。 如果你希望发布自己的插件,建议使用 nvm 或 nvs (适用于 Windows)
如果你要使用 Metalsmith CLI,请在 PATH
环境变量中添加 node_modules/.bin
路径:
- 在 Windows 下,点击 Windows 键并搜索 编辑环境变量,然后在
PATH
变量中添加node_modules\\.bin
路径 - 在 Linux/Mac 上,将
export PATH=node_modules/.bin;$PATH
添加到~/.bashrc
或~/.bash_profile
文件中,然后执行source
命令。
Metalsmith 已在 Linux 和 Windows 系统以及所有 官方支持的 NodeJS LTS 版本 上进行了测试。
安装
首先,为新项目创建一个项目文件夹并切换至该文件夹下:
然后使用任何 NodeJS 软件包管理器安装 Metalsmith:
可能还需要安装一些插件:
如果你想使用 Typescript 写代码,请务必同时安装 @types/metalsmith
软件包。
概念
要了解如何使用 Metalsmith,你只需要熟悉几个核心概念,如果你使用过其他静态网站生成器,或者在终端中运行过一些命令(例如 git add
或 ls -la
),你可能已经熟悉了其中的大部分了。
Files
Everyone has worked with files before. They have a name, a path, an extension, contents and metadata (like the last modified date). Metalsmith represents every file in the source directory as a javascript File object. For instance,
src/post/my-file.md:
---
title: A Catchy Title
draft: false
---
An unfinished article...
...becomes
{
'post/my-file.md': {
title: 'A Catchy Title',
draft: false,
contents: 'An unfinished article...',
mode: '0664',
stats: {
/* keys with information on the file */
}
}
}
...where the content of the file is always mapped to the property value of contents
. For illustration purposes only we display the value of contents
as a string. Technically, the property value of contents
is mapped as a NodeJS Buffer
, which can also handle binary data (for images, PDF's, etc). mode
contains the file permission bit and stats
has more technical information on the file such as size
or birthtime
. The file is also parsed for YAML front matter, which is merged into the File object. Thus, we finally have a javascript Files object of objects.
{
'relative_to_sourcepath/file1.md': {
title: 'A Catchy Title',
draft: false,
contents: 'An unfinished article...',
mode: '0664',
stats: {
/* keys with information on the file */
}
},
'relative_to_sourcepath/file2.md': {
title: 'An Even Better Title',
draft: false,
contents: 'One more unfinished article...',
mode: '0664',
stats: {
/* keys with information on the file */
}
}
}
Plugins can then manipulate the javascript File objects representing the original files however they want, and writing a plugin is super simple.
Front matter
To attach metadata to a JS File object, metalsmith reads front matter. Front matter is a term borrowed from the publishing industry meaning metadata about a written work. In Metalsmith this is a YAML document section (delineated by ---
) containing metadata (matter) at the top (front) of a file (commonly, markdown). Metalsmith will recognize and read the front matter of a file and add it as metadata to the JS file representation when you run the build. Here is a typical example of an index.md
file with YAML front-matter. If you don't like the YAML syntax you can use JSON front matter as well
The front-matter will be parsed by Metalsmith as:
{
title: 'Hello World',
keywords: ['hello','world'],
draft: false,
contents: <Buffer>,
mode: '0644',
stats: { ... }
}
When the front matter is read into javascript, we refer to it as file metadata.
Multi-line strings
A common requirement is to write multi-line strings in YAML, either for readability or for output. There are a lot of ways to write multiline strings in YAML. Examples of the two most common ones are shown here:
Using the greater than sign
>
(removing line breaks)description: > This is a long text string, but we actually only want the line breaks for readability in the source. The line breaks will be removed in the output.
Using the pipe sign
|
(preserving line breaks)description: | This is a long text string. It should read like a poem So we want to preserve the line breaks.
Glob patterns
Metalsmith and its plugins make extensive use of glob patterns to target specific files (usually through the pattern
option). A glob is a type of string pattern syntax that is commonly and conveniently used to match files by path with support for globstar wildcards *
. Chances are you are already using glob patterns in .gitignore
files, with the Linux/Mac or git
terminal commands. Here are a few examples of how you can match with glob patterns:
Matching all files (the double globstar is recursive): see it in action
**/*
Matching all files at the root of a directory with a
.html
extension: see it in action*.html
Matching all markdown files starting with
post-
under theblog/posts
folder: see it in actionblog/posts/post-*.md
Matching either ... or ...: see it in action
{services/**,blog}/index.md
Matching all except markdown files (negated match): see it in action
!**/*.md
You can always use DigitalOcean's handy Glob tool or globster.xyz to test your glob patterns.
The plugin chain
We believe that understanding the internal representation of files as JavaScript objects is really key to fully grasp the concept of Metalsmith. To understand this better, we follow the evolution of a file at each step of the build process (between each use
statement). We are also using the writemetadata()
plugin, which writes the {key: value}
pairs excerpted from the File objects representing the files, to the filesystem as .json
files. You can then view the .json
files to find out how files are represented internally in Metalsmith.
In the example above, after applying .use(markdown())
the initial representation of my-file.md
becomes my-file.html
. The markdown plugin changes the file extension and converts the contents to HTML.
{
'relative_to_sourcepath/my-file.html': {
title: 'A Catchy Title',
draft: false,
contents: '<p>An unfinished article...</p>',
...
}
}
After applying .use(permalinks())
the file is renamed to original-name/index.html
and a path
property is added to the file's metadata:
{
'relative_to_sourcepath/my-file/index.html': {
title: 'A Catchy Title',
draft: false,
contents: '<p>An unfinished article...</p>',
path: 'myfile',
...
}
}
Assuming we have defined a very simple nunjucks layout file in a separate layouts folder...
... after applying .use(layouts())
in our Metalsmith chain our JavaScript object becomes:
{
'relative_to_sourcepath/my-file/index.html': {
title: 'A Catchy Title',
draft: false,
contents: `<!doctype html><html><head>
<title>A Catchy Title</title></head><body>
<p>An unfinished article...</p>
</body></html>`,
path: 'myfile',
...
}
}
Finally when the .build(function(err))
is performed our JavaScript object is written to relative_to_destpath/myfile/index.html
.
Quickstart
You want to build a website or blog with a static site generator. Well, here is our elevator pitch. It's as easy as that:
Directory structure
A typical directory structure for a metalsmith (static-site) project looks more or less like this:
repo
├── metalsmith.js
├── package.json
├── node_modules
├── build
├── layouts
│ ├── default.hbs
│ └── post.hbs
├── lib
│ └── sass
│ │ └── style.scss
│ ├── data
│ │ └── nav.json
│ └── plugins
│ └── local-metalsmith-plugin.js
└── src
├── about.md
├── index.md
└── posts
├── first-post.md
└── second-post.md
where:
repo
is yourmetalsmith.directory()
— the root of the project, with all dependencies, config files, etc.src
is yourmetalsmith.source()
— it contains the contents that metalsmith needs to process and output tobuild
build
is yourmetalsmith.destination()
— it is created dynamically when you first runmetalsmith.build
lib
is for all the rest (for example local metalsmith plugins, Sass styles that are processed outside of metalsmith and added to the build, or metadata that is manuallyrequire()
'd)
...but Metalsmith gives you total freedom about how you want to structure your project, so feel free to restructure things as you see fit.
Starter projects
The community has built a few interesting starter projects:
- metalsmith/startbootstrap-clean-blog: a stylish, responsive blog theme for Bootstrap created by Start Bootstrap, and made customizable with metalsmith. | Demo
- wernerglinka/metalsmith-bare-bones-starter: Bare-bones metalsmith starter with markdown & Nunjucks templating | Demo
- wernerglinka/metalsmith-blog-starter: Blog metalsmith starter with markdown & Nunjucks templating + a landing page & some sample articles | Demo
- wernerglinka/metalsmith-company-starter: Company site starter with markdown & Nunjucks templating | Demo
- webketje/metalsmith-starter-resume: A professional, responsive Bootstrap4-themed resume / CV, made highly customizable with metalsmith, Handlebars & SCSS. Features print-friendly version, and twitter/ facebook/ linkedin share tags.. | Demo
There is also a one-click Netlify CMS starter.