xfmpress 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.1] - 2026-11-03
8
+
9
+ ### Changed
10
+
11
+ - English `README.md` is now the default document shown on the npm page;
12
+ the Chinese version ships as `README_CN.md`, and both link to each other.
13
+ - Documentation split into English/Chinese pairs: `README` / `README_CN`,
14
+ `INSTALL` / `INSTALL_CN`, `PROGRAM` / `PROGRAM_CN`.
15
+ - Dev server port corrected to `4173` in all docs and scaffolds
16
+ (previously documented inconsistently as `4000` / `4321`).
17
+ - Package description rewritten in English to match the npm-facing README.
18
+
19
+ ### Fixed
20
+
21
+ - Docs referenced the wrong install target (`npm i -D xfm`); the installable
22
+ package is `xfmpress` while the CLI command remains `xfm`.
23
+ - Configuration examples now consistently use the `site: {}` shape with
24
+ a top-level `outDir`.
25
+
26
+ ## [0.1.0] - 2026-11-02
27
+
28
+ ### Added
29
+
30
+ - Initial release: zero-dependency static site generator with a shared
31
+ render pipeline used by both the main thread and worker threads.
32
+ - Multi-threaded page rendering with content-hashed incremental builds.
33
+ - Nunjucks-style template engine compiled to JavaScript functions at build
34
+ time, with optional-chaining guards generated at compile time.
35
+ - Plugin system with `markdown`, `transformHtml`, `extendContext`,
36
+ `afterBuild`, `configureServer` and `finish` hooks.
37
+ - Built-in `default` and `docs` themes, plus `xfm create` scaffolding.
38
+ - Dev server with SSE live reload and configuration hot reload.
39
+ - `xfm new`, `xfm build`, `xfm dev`, `xfm clean` commands.
40
+
41
+ [0.1.1]: https://github.com/xfm0797/xfm/compare/v0.1.0...v0.1.1
42
+ [0.1.0]: https://github.com/xfm0797/xfm/releases/tag/v0.1.0
package/INSTALL.md CHANGED
@@ -1,81 +1,83 @@
1
- # xfm 安装与使用说明
1
+ # xfm — Installation & Usage
2
2
 
3
- > 面向 Node 22 环境,从零到跑起来大约 1 分钟。
4
- > 项目地址:`https://github.com/xfm0797/xfm`
3
+ > Assumes a Node 22 environment. From zero to a running site takes about a minute.
4
+ > Repository: `https://github.com/xfm0797/xfm`
5
+
6
+ [English](./INSTALL.md) | [简体中文](./INSTALL_CN.md)
5
7
 
6
8
  ---
7
9
 
8
- ## 一、先决条件
10
+ ## 1. Prerequisites
9
11
 
10
- | 项目 | 要求 | 检查命令 |
12
+ | Item | Requirement | Check |
11
13
  | --- | --- | --- |
12
- | Node.js | **18 或更高**(推荐 22 LTS) | `node -v` |
13
- | npm | 随 Node 附带(推荐 ≥ 9) | `npm -v` |
14
+ | Node.js | **18 or newer** (22 LTS recommended) | `node -v` |
15
+ | npm | Ships with Node (>= 9 recommended) | `npm -v` |
14
16
 
15
17
  ```bash
16
- node -v # 期望 v22.x
17
- npm -v # 期望 10.x 或 11.x
18
+ node -v # expect v22.x
19
+ npm -v # expect 10.x or 11.x
18
20
  ```
19
21
 
20
- Node 版本过旧会报 `EBADENGINE`,用 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/Schniz/fnm) 切到 22 即可:
22
+ An outdated Node version triggers `EBADENGINE`. Switch to 22 with [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm):
21
23
 
22
24
  ```bash
23
25
  nvm install 22 && nvm use 22
24
26
  ```
25
27
 
26
- xfm 是**零运行时依赖**的,安装时不会拉取任何第三方包,也因此不会出现依赖树冲突、`npm audit` 报警。
28
+ xfm has **zero runtime dependencies**, so installing it pulls no third-party packages — which also means no dependency-tree conflicts and no `npm audit` warnings.
27
29
 
28
30
  ---
29
31
 
30
- ## 二、安装(三种方式,按需选一种)
32
+ ## 2. Install (pick one of three methods)
31
33
 
32
- ### 方式 A:全局安装 —— 和 Hexo 一样用 `xfm` 命令
34
+ ### Option A: Global install — use `xfm` like you use `hexo`
33
35
 
34
36
  ```bash
35
37
  npm install -g xfmpress
36
38
  ```
37
39
 
38
- 验证:
40
+ Verify:
39
41
 
40
42
  ```bash
41
43
  xfm --help
42
44
  ```
43
45
 
44
- 看到 `⚡ xfm v0.1.0` 的版本横幅即安装成功。
46
+ If you see the `⚡ xfm v0.1.0` banner, you are good to go.
45
47
 
46
- > **命名说明**:npm 包名是 **`xfmpress`**,命令是 **`xfm`**(`npm i -g xfmpress` 之后用 `xfm` 调用)。定位是 Static Site Generator(静态站点生成器),与同系列的 `hexo-theme-xfm` 共用品牌。项目早期叫 `bolt`,`bolt`/`bolt-ssg` 已被占用,`xmssg` 与 `xfm` 又分别被 npm 的"名字过于相似"校验拦下(撞 `xss`、以及 `xo`/`xml`/`htm` 等一批短包名),0.1.0 起定为 `xfmpress`。
48
+ > **About the name**: the npm package is **`xfmpress`**, the command is **`xfm`** (`npm i -g xfmpress`, then call `xfm`). It is a Static Site Generator, sharing the `xfm` brand with the `hexo-theme-xfm` family. The project was originally called `bolt`; `bolt`/`bolt-ssg` were taken, and `xmssg` and `xfm` were both rejected by npm's "too similar to an existing package" check (against `xss`, then against `xo`/`xml`/`htm` and friends), so `xfmpress` has been the name since 0.1.0.
47
49
 
48
- **权限报错怎么办**(`EACCES: permission denied`)?有两种正规解法,不要用 `sudo npm i -g`:
50
+ **Hit `EACCES: permission denied`?** There are two proper fixes; do not use `sudo npm i -g`:
49
51
 
50
52
  ```bash
51
- # 解法 1(推荐):把全局前缀改到用户目录
53
+ # Fix 1 (recommended): move the global prefix into your home directory
52
54
  npm config set prefix ~/.npm-global
53
- echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.zshrc # bash 用户改成 ~/.bashrc
55
+ echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.zshrc # use ~/.bashrc for bash
54
56
  source ~/.zshrc
55
57
 
56
- # 解法 2:用 nvm 管理 Node(全局包天然在用户目录,无需额外配置)
58
+ # Fix 2: manage Node with nvm (global packages already live in your home directory)
57
59
  ```
58
60
 
59
- ### 方式 B:免安装运行 —— 只想先试试
61
+ ### Option B: Run without installing — just want to try it
60
62
 
61
63
  ```bash
62
64
  npx xfmpress --help
63
65
  npx xfmpress create my-blog --template=blog
64
66
  ```
65
67
 
66
- `npx` 会自动下载到临时缓存并执行,不在系统里留下全局命令。适合快速体验或 CI 里一次性构建。
68
+ `npx` downloads into a temporary cache and runs from there, leaving no global command behind. Good for a quick look or a one-off build in CI.
67
69
 
68
- ### 方式 C:项目内安装 —— 团队协作推荐
70
+ ### Option C: Install into a project — best for teams
69
71
 
70
- 把 xfm 作为开发依赖写进项目,任何人 `npm install` 后即可获得**完全一致**的版本:
72
+ Add xfm as a dev dependency so everyone gets the **exact same** version after `npm install`:
71
73
 
72
74
  ```bash
73
75
  mkdir my-blog && cd my-blog
74
76
  npm init -y
75
- npm i -D xfm
77
+ npm i -D xfmpress
76
78
  ```
77
79
 
78
- 然后在 `package.json` 里加脚本:
80
+ Then add scripts to `package.json`:
79
81
 
80
82
  ```json
81
83
  {
@@ -88,201 +90,207 @@ npm i -D xfm
88
90
  }
89
91
  ```
90
92
 
91
- 之后用 `npm run dev` / `npm run build` 即可;临时调用可用 `npx xfmpress info`。
93
+ From then on use `npm run dev` / `npm run build`; for a one-off command use `npx xfmpress info`.
92
94
 
93
- > 方式 C 的缺点是不能在空目录里用 `xfm create` 生成脚手架(那时还没有 `node_modules`)。建议先用方式 A 或 B 生成站点,再进目录装成本地依赖。
95
+ > The downside of Option C: you cannot run `xfm create` in an empty directory, because there is no `node_modules` yet. Scaffold the site with Option A or B first, then install it locally.
94
96
 
95
97
  ---
96
98
 
97
- ## 三、快速上手
99
+ ## 3. Quick start
98
100
 
99
- ### 1. 创建站点
101
+ ### 1. Create a site
100
102
 
101
103
  ```bash
102
104
  xfm create my-blog --template=blog
103
105
  cd my-blog
104
106
  ```
105
107
 
106
- 可选模板:
108
+ Available templates:
107
109
 
108
- | `--template` | 外观 | 适合 |
110
+ | `--template` | Look | Good for |
109
111
  | --- | --- | --- |
110
- | `blog`(默认) | 博客:首页列表 + 文章页 + 右侧目录 | 个人博客、技术随笔 |
111
- | `docs` | 文档:左侧分组侧栏 + 正文 | 产品文档、知识库 |
112
+ | `blog` (default) | Blog: post list on the home page, post pages, right-hand TOC | Personal blogs, tech notes |
113
+ | `docs` | Documentation: grouped left sidebar + body | Product docs, knowledge bases |
112
114
 
113
- 也能指定站名:
115
+ You can also set the site title:
114
116
 
115
117
  ```bash
116
- xfm create my-blog --template=docs --title="我的知识库"
118
+ xfm create my-blog --template=docs --title="My Knowledge Base"
117
119
  ```
118
120
 
119
- 生成的结构:
121
+ Generated structure:
120
122
 
121
123
  ```
122
124
  my-blog/
123
- ├── xfm.config.mjs # 站点配置(有合理默认值,可按需改)
124
- ├── content/ # 所有 Markdown 源文件
125
+ ├── xfm.config.mjs # site config (sensible defaults, edit as needed)
126
+ ├── content/ # all Markdown sources
125
127
  │ ├── index.md
126
128
  │ ├── about.md
127
129
  │ └── posts/
128
130
  │ └── hello-world.md
129
- ├── plugins/ # 你自己的插件
130
- ├── public/ # 原样拷贝到产物根目录的静态文件
131
+ ├── plugins/ # your own plugins
132
+ ├── public/ # static files copied verbatim to the output root
131
133
  └── README.md
132
134
  ```
133
135
 
134
- ### 2. 本地开发
136
+ ### 2. Local development
135
137
 
136
138
  ```bash
137
139
  xfm dev
138
140
  ```
139
141
 
140
- 打开 `http://127.0.0.1:4000/`。改 `content/` 里的 Markdown 或 `themes/` 里的模板,保存后浏览器**自动刷新**(SSE 推送,无需装浏览器插件)。
142
+ Open `http://127.0.0.1:4173/`. Edit Markdown under `content/` or templates under `themes/`, save, and the browser **refreshes itself** (SSE push, no browser extension needed).
141
143
 
142
144
  ```bash
143
- xfm dev --port=5000 # 换端口
145
+ xfm dev --port=5000 # different port
144
146
  ```
145
147
 
146
- ### 3. 写文章
148
+ ### 3. Write a post
147
149
 
148
150
  ```bash
149
- xfm new "我的第一篇文章"
150
- # ✓ 已创建 content/posts/我的第一篇文章.md (文件名由标题自动转为 slug)
151
+ xfm new "My First Post"
152
+ # ✓ created content/posts/my-first-post.md (the title is slugified into the filename)
151
153
  ```
152
154
 
153
- 带空格或多语言的标题会自动处理文件名:
155
+ Titles with spaces or non-ASCII characters are handled automatically:
154
156
 
155
157
  ```bash
156
158
  xfm new "Hello, World!" # → content/posts/hello-world.md
157
- xfm new "部署笔记" --slug=deploy-notes # → 手动指定文件名
158
- xfm new "关于" --dir=pages # → 放到 content/pages/ 下
159
+ xfm new "Deploy notes" --slug=deploy-notes # → explicit filename
160
+ xfm new "About" --dir=pages # → under content/pages/
159
161
  ```
160
162
 
161
- > **注意**:`xfm new` 生成的文章**默认可发布**,`xfm build` 立刻就能看到。想先写半成品,用 `xfm new --draft`(会写入 `draft: true`),这类文章不会出现在生产构建里;要发布时把 `draft` 改成 `false` 或删掉该行即可。
163
+ > **Note**: posts created by `xfm new` are **publishable by default** and show up on the next `xfm build`. To write a work in progress, use `xfm new --draft` (writes `draft: true`); such posts never appear in production builds. Set `draft` to `false` or delete the line when you are ready to publish.
162
164
 
163
- ### 4. 生产构建
165
+ ### 4. Production build
164
166
 
165
167
  ```bash
166
168
  xfm build
167
169
  ```
168
170
 
169
- 产物在 `dist/`,纯静态,扔到 Nginx / 对象存储 / Vercel / Netlify / GitHub Pages 都能跑。
171
+ Output goes to `dist/`, fully static — drop it on Nginx, object storage, Vercel, Netlify or GitHub Pages.
170
172
 
171
173
  ```
172
- ✓ 页面 5 个
173
- ✓ 附加产物 4 个(sitemap / rss 等)
174
- ✓ 输出目录 dist
175
- 耗时 30 ms
174
+ ✓ pages 5
175
+ ✓ extra files 4 (sitemap / rss, etc.)
176
+ ✓ output dist
177
+ done in 30 ms
176
178
  ```
177
179
 
178
- 重构建会走增量缓存,只重新渲染改动过的页面:
180
+ Rebuilds use the incremental cache and only re-render changed pages:
179
181
 
180
182
  ```bash
181
- xfm build --dev # 开发模式下也启用增量缓存
183
+ xfm build --dev # enable the incremental cache in dev mode too
182
184
  ```
183
185
 
184
- ### 5. 其他命令
186
+ ### 5. Other commands
185
187
 
186
188
  ```bash
187
- xfm info # 查看当前站点解析后的配置(主题、内容目录、插件、Node 版本)
188
- xfm clean # 清空 dist/ 与增量缓存
189
- xfm --help # 全部命令与选项
189
+ xfm info # show the resolved config (theme, content dir, plugins, Node version)
190
+ xfm clean # wipe dist/ and the incremental cache
191
+ xfm --help # every command and option
190
192
  ```
191
193
 
192
194
  ---
193
195
 
194
- ## 四、写 Markdown
196
+ ## 4. Writing Markdown
195
197
 
196
- 内容就是标准 Markdown,没有私有语法。常用增强:
198
+ Content is plain Markdown — no proprietary syntax. Common extensions:
197
199
 
198
200
  ````markdown
199
201
  ---
200
- title: 文章标题
202
+ title: Post title
201
203
  date: 2024-06-01
202
- tags: [性能, Node]
203
- description: 用于 SEO 与列表摘要
204
+ tags: [performance, node]
205
+ description: Used for SEO and list summaries
204
206
  layout: post.html
205
207
  draft: false
206
208
  ---
207
209
 
208
- ## 二级标题会自动进右侧目录
210
+ ## H2 headings land in the right-hand TOC automatically
209
211
 
210
- 普通段落支持 **加粗**、*斜体*、`行内代码`、~~删除线~~、[链接](/about/)。
212
+ Normal paragraphs support **bold**, *italic*, `inline code`, ~~strikethrough~~, [links](/about/).
211
213
 
212
- | 语法 | 说明 |
214
+ | Syntax | Notes |
213
215
  | --- | --- |
214
- | 表格 | 标准 GFM |
216
+ | Tables | Standard GFM |
215
217
 
216
- - [x] 任务列表已支持
217
- - [ ] 未完成项
218
+ - [x] Task lists work
219
+ - [ ] Unfinished item
218
220
 
219
- ::: tip 提示容器
220
- 支持 `tip` / `info` / `warning` / `danger` 四种。
221
+ ::: tip Callout
222
+ Four kinds: `tip` / `info` / `warning` / `danger`.
221
223
  :::
222
224
 
223
225
  ```js
224
- const ok = true; // 代码块自动语法高亮
226
+ const ok = true; // code blocks are highlighted automatically
225
227
  ```
226
228
  ````
227
229
 
228
- 代码高亮支持:`js / javascript / jsx / ts / tsx / java / c / cpp / csharp / go / rust / swift / kotlin / scala / php / python / bash / sh / json / yaml / html / xml / svg / vue / css / scss / markdown`,大小写与常见别名都能识别;未知语言降级为纯文本,不会报错。围栏可加标题:`` ```js app.js ``。
230
+ Highlighting supports: `js / javascript / jsx / ts / tsx / java / c / cpp / csharp / go / rust / swift / kotlin / scala / php / python / bash / sh / json / yaml / html / xml / svg / vue / css / scss / markdown`, case-insensitive and alias-aware; unknown languages degrade to plain text instead of failing. Fences can carry a title: `` ```js app.js ``.
229
231
 
230
232
  ---
231
233
 
232
- ## 五、配置
234
+ ## 5. Configuration
233
235
 
234
- `xfm.config.mjs` 是一个普通 ESM 模块,导出对象即可,改动后 dev 服务器会自动重启:
236
+ `xfm.config.mjs` is a plain ESM module — export an object and you are done. The dev server picks up changes automatically:
235
237
 
236
238
  ```js
237
239
  export default {
238
- title: '我的博客',
239
- description: '记录技术与生活',
240
- url: 'https://example.com', // 用于 sitemap / rss 的绝对链接
241
- theme: 'default', // 内置主题:default | docs;也可填本地路径
240
+ site: {
241
+ title: 'My Blog',
242
+ description: 'Notes on tech and life',
243
+ url: 'https://example.com', // absolute links for sitemap / rss
244
+ lang: 'en',
245
+ base: '/',
246
+ nav: [{ title: 'Home', url: '/' }],
247
+ },
242
248
  contentDir: 'content',
243
- outputDir: 'dist',
249
+ outDir: 'dist',
250
+ theme: 'default', // built-in: default | docs; or a local path
244
251
  plugins: [
245
252
  'sitemap',
246
253
  'rss',
247
- './plugins/my-plugin.js', // 本地插件
254
+ './plugins/my-plugin.js', // local plugin
248
255
  ],
249
- workers: 0, // 0 = 自动;渲染进程数
256
+ workers: 0, // 0 = auto; number of render workers
257
+ server: { port: 4173 }, // dev port
250
258
  };
251
259
  ```
252
260
 
253
- 所有字段都有默认值,最小配置就是一个空对象。完整字段见 README。
261
+ Every field has a default, so the minimal config is an empty object. Full reference: `README.md` (English) / `README_CN.md` (Chinese).
254
262
 
255
263
  ---
256
264
 
257
- ## 六、常见问题
265
+ ## 6. FAQ
258
266
 
259
267
  **`xfm: command not found`**
260
- 全局 npm 的 `bin` 目录不在 `PATH` 里。执行 `npm bin -g`(旧版)或 `npm config get prefix`,把其下的 `bin` 加进 `PATH`。用 nvm 的话重开一个终端通常就能解决。
268
+ The global npm `bin` directory is not on your `PATH`. Run `npm config get prefix` and add its `bin` subdirectory to `PATH`. With nvm, opening a fresh terminal usually fixes it.
261
269
 
262
- **安装时报 `EACCES: permission denied`**
263
- 见「方式 A」里的全局前缀改法,不要用 `sudo`。
270
+ **`EACCES: permission denied` during install**
271
+ See the global-prefix fix under Option A. Do not use `sudo`.
264
272
 
265
- **`node -v` 低于 18**
266
- 升级 Node 后重装:`npm i -g xfmpress --force`。
273
+ **`node -v` is below 18**
274
+ Upgrade Node, then reinstall: `npm i -g xfmpress --force`.
267
275
 
268
- **`xfm new` 建了文章但页面上没有**
269
- 大概率是 `draft: true`。改成 `false` 后重新 `xfm build`。
276
+ **`xfm new` created a post but it is not on the site**
277
+ Most likely `draft: true`. Change it to `false` and run `xfm build` again.
270
278
 
271
- **Windows 下 `xfm` 提示不是内部或外部命令**
272
- 用 Git Bash 或 PowerShell 重开终端;仍不行则检查 `%APPDATA%\npm` 是否在 `Path` 中。
279
+ **On Windows, `xfm` is "not recognized as an internal or external command"**
280
+ Reopen your terminal in Git Bash or PowerShell. If it still fails, check that `%APPDATA%\npm` is on your `Path`.
273
281
 
274
- **想升级**
282
+ **Upgrading**
275
283
  ```bash
276
- npm i -g xfmpress@latest # 全局
277
- npm i -D xfm@latest # 项目内
284
+ npm i -g xfmpress@latest # global
285
+ npm i -D xfmpress@latest # in a project
278
286
  ```
279
287
 
280
288
  ---
281
289
 
282
- ## 七、一键脚本(复制即用)
290
+ ## 7. One-liner (copy & paste)
283
291
 
284
292
  ```bash
285
- # 全局安装 + 创建博客 + 本地预览
293
+ # install globally + scaffold a blog + start the dev server
286
294
  npm i -g xfmpress \
287
295
  && xfm create my-blog --template=blog \
288
296
  && cd my-blog \
package/INSTALL_CN.md ADDED
@@ -0,0 +1,298 @@
1
+ # xfm 安装与使用说明
2
+
3
+ > 面向 Node 22 环境,从零到跑起来大约 1 分钟。
4
+ > 项目地址:`https://github.com/xfm0797/xfm`
5
+
6
+ [English](./INSTALL.md) | 简体中文
7
+
8
+ ---
9
+
10
+ ## 一、先决条件
11
+
12
+ | 项目 | 要求 | 检查命令 |
13
+ | --- | --- | --- |
14
+ | Node.js | **18 或更高**(推荐 22 LTS) | `node -v` |
15
+ | npm | 随 Node 附带(推荐 ≥ 9) | `npm -v` |
16
+
17
+ ```bash
18
+ node -v # 期望 v22.x
19
+ npm -v # 期望 10.x 或 11.x
20
+ ```
21
+
22
+ Node 版本过旧会报 `EBADENGINE`,用 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/Schniz/fnm) 切到 22 即可:
23
+
24
+ ```bash
25
+ nvm install 22 && nvm use 22
26
+ ```
27
+
28
+ xfm 是**零运行时依赖**的,安装时不会拉取任何第三方包,也因此不会出现依赖树冲突、`npm audit` 报警。
29
+
30
+ ---
31
+
32
+ ## 二、安装(三种方式,按需选一种)
33
+
34
+ ### 方式 A:全局安装 —— 和 Hexo 一样用 `xfm` 命令
35
+
36
+ ```bash
37
+ npm install -g xfmpress
38
+ ```
39
+
40
+ 验证:
41
+
42
+ ```bash
43
+ xfm --help
44
+ ```
45
+
46
+ 看到 `⚡ xfm v0.1.0` 的版本横幅即安装成功。
47
+
48
+ > **命名说明**:npm 包名是 **`xfmpress`**,命令是 **`xfm`**(`npm i -g xfmpress` 之后用 `xfm` 调用)。定位是 Static Site Generator(静态站点生成器),与同系列的 `hexo-theme-xfm` 共用品牌。项目早期叫 `bolt`,`bolt`/`bolt-ssg` 已被占用,`xmssg` 与 `xfm` 又分别被 npm 的"名字过于相似"校验拦下(撞 `xss`、以及 `xo`/`xml`/`htm` 等一批短包名),0.1.0 起定为 `xfmpress`。
49
+
50
+ **权限报错怎么办**(`EACCES: permission denied`)?有两种正规解法,不要用 `sudo npm i -g`:
51
+
52
+ ```bash
53
+ # 解法 1(推荐):把全局前缀改到用户目录
54
+ npm config set prefix ~/.npm-global
55
+ echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.zshrc # bash 用户改成 ~/.bashrc
56
+ source ~/.zshrc
57
+
58
+ # 解法 2:用 nvm 管理 Node(全局包天然在用户目录,无需额外配置)
59
+ ```
60
+
61
+ ### 方式 B:免安装运行 —— 只想先试试
62
+
63
+ ```bash
64
+ npx xfmpress --help
65
+ npx xfmpress create my-blog --template=blog
66
+ ```
67
+
68
+ `npx` 会自动下载到临时缓存并执行,不在系统里留下全局命令。适合快速体验或 CI 里一次性构建。
69
+
70
+ ### 方式 C:项目内安装 —— 团队协作推荐
71
+
72
+ 把 xfm 作为开发依赖写进项目,任何人 `npm install` 后即可获得**完全一致**的版本:
73
+
74
+ ```bash
75
+ mkdir my-blog && cd my-blog
76
+ npm init -y
77
+ npm i -D xfmpress
78
+ ```
79
+
80
+ 然后在 `package.json` 里加脚本:
81
+
82
+ ```json
83
+ {
84
+ "scripts": {
85
+ "dev": "xfm dev",
86
+ "build": "xfm build",
87
+ "new": "xfm new",
88
+ "clean": "xfm clean"
89
+ }
90
+ }
91
+ ```
92
+
93
+ 之后用 `npm run dev` / `npm run build` 即可;临时调用可用 `npx xfmpress info`。
94
+
95
+ > 方式 C 的缺点是不能在空目录里用 `xfm create` 生成脚手架(那时还没有 `node_modules`)。建议先用方式 A 或 B 生成站点,再进目录装成本地依赖。
96
+
97
+ ---
98
+
99
+ ## 三、快速上手
100
+
101
+ ### 1. 创建站点
102
+
103
+ ```bash
104
+ xfm create my-blog --template=blog
105
+ cd my-blog
106
+ ```
107
+
108
+ 可选模板:
109
+
110
+ | `--template` | 外观 | 适合 |
111
+ | --- | --- | --- |
112
+ | `blog`(默认) | 博客:首页列表 + 文章页 + 右侧目录 | 个人博客、技术随笔 |
113
+ | `docs` | 文档:左侧分组侧栏 + 正文 | 产品文档、知识库 |
114
+
115
+ 也能指定站名:
116
+
117
+ ```bash
118
+ xfm create my-blog --template=docs --title="我的知识库"
119
+ ```
120
+
121
+ 生成的结构:
122
+
123
+ ```
124
+ my-blog/
125
+ ├── xfm.config.mjs # 站点配置(有合理默认值,可按需改)
126
+ ├── content/ # 所有 Markdown 源文件
127
+ │ ├── index.md
128
+ │ ├── about.md
129
+ │ └── posts/
130
+ │ └── hello-world.md
131
+ ├── plugins/ # 你自己的插件
132
+ ├── public/ # 原样拷贝到产物根目录的静态文件
133
+ └── README.md
134
+ ```
135
+
136
+ ### 2. 本地开发
137
+
138
+ ```bash
139
+ xfm dev
140
+ ```
141
+
142
+ 打开 `http://127.0.0.1:4173/`。改 `content/` 里的 Markdown 或 `themes/` 里的模板,保存后浏览器**自动刷新**(SSE 推送,无需装浏览器插件)。
143
+
144
+ ```bash
145
+ xfm dev --port=5000 # 换端口
146
+ ```
147
+
148
+ ### 3. 写文章
149
+
150
+ ```bash
151
+ xfm new "我的第一篇文章"
152
+ # ✓ 已创建 content/posts/我的第一篇文章.md (文件名由标题自动转为 slug)
153
+ ```
154
+
155
+ 带空格或多语言的标题会自动处理文件名:
156
+
157
+ ```bash
158
+ xfm new "Hello, World!" # → content/posts/hello-world.md
159
+ xfm new "部署笔记" --slug=deploy-notes # → 手动指定文件名
160
+ xfm new "关于" --dir=pages # → 放到 content/pages/ 下
161
+ ```
162
+
163
+ > **注意**:`xfm new` 生成的文章**默认可发布**,`xfm build` 立刻就能看到。想先写半成品,用 `xfm new --draft`(会写入 `draft: true`),这类文章不会出现在生产构建里;要发布时把 `draft` 改成 `false` 或删掉该行即可。
164
+
165
+ ### 4. 生产构建
166
+
167
+ ```bash
168
+ xfm build
169
+ ```
170
+
171
+ 产物在 `dist/`,纯静态,扔到 Nginx / 对象存储 / Vercel / Netlify / GitHub Pages 都能跑。
172
+
173
+ ```
174
+ ✓ 页面 5 个
175
+ ✓ 附加产物 4 个(sitemap / rss 等)
176
+ ✓ 输出目录 dist
177
+ 耗时 30 ms
178
+ ```
179
+
180
+ 重构建会走增量缓存,只重新渲染改动过的页面:
181
+
182
+ ```bash
183
+ xfm build --dev # 开发模式下也启用增量缓存
184
+ ```
185
+
186
+ ### 5. 其他命令
187
+
188
+ ```bash
189
+ xfm info # 查看当前站点解析后的配置(主题、内容目录、插件、Node 版本)
190
+ xfm clean # 清空 dist/ 与增量缓存
191
+ xfm --help # 全部命令与选项
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 四、写 Markdown
197
+
198
+ 内容就是标准 Markdown,没有私有语法。常用增强:
199
+
200
+ ````markdown
201
+ ---
202
+ title: 文章标题
203
+ date: 2024-06-01
204
+ tags: [性能, Node]
205
+ description: 用于 SEO 与列表摘要
206
+ layout: post.html
207
+ draft: false
208
+ ---
209
+
210
+ ## 二级标题会自动进右侧目录
211
+
212
+ 普通段落支持 **加粗**、*斜体*、`行内代码`、~~删除线~~、[链接](/about/)。
213
+
214
+ | 语法 | 说明 |
215
+ | --- | --- |
216
+ | 表格 | 标准 GFM |
217
+
218
+ - [x] 任务列表已支持
219
+ - [ ] 未完成项
220
+
221
+ ::: tip 提示容器
222
+ 支持 `tip` / `info` / `warning` / `danger` 四种。
223
+ :::
224
+
225
+ ```js
226
+ const ok = true; // 代码块自动语法高亮
227
+ ```
228
+ ````
229
+
230
+ 代码高亮支持:`js / javascript / jsx / ts / tsx / java / c / cpp / csharp / go / rust / swift / kotlin / scala / php / python / bash / sh / json / yaml / html / xml / svg / vue / css / scss / markdown`,大小写与常见别名都能识别;未知语言降级为纯文本,不会报错。围栏可加标题:`` ```js app.js ``。
231
+
232
+ ---
233
+
234
+ ## 五、配置
235
+
236
+ `xfm.config.mjs` 是一个普通 ESM 模块,导出对象即可,改动后 dev 服务器会自动重启:
237
+
238
+ ```js
239
+ export default {
240
+ site: {
241
+ title: '我的博客',
242
+ description: '记录技术与生活',
243
+ url: 'https://example.com', // 用于 sitemap / rss 的绝对链接
244
+ lang: 'zh-CN',
245
+ base: '/',
246
+ nav: [{ title: '首页', url: '/' }],
247
+ },
248
+ contentDir: 'content',
249
+ outDir: 'dist',
250
+ theme: 'default', // 内置主题:default | docs;也可填本地路径
251
+ plugins: [
252
+ 'sitemap',
253
+ 'rss',
254
+ './plugins/my-plugin.js', // 本地插件
255
+ ],
256
+ workers: 0, // 0 = 自动;渲染进程数
257
+ server: { port: 4173 }, // dev 端口
258
+ };
259
+ ```
260
+
261
+ 所有字段都有默认值,最小配置就是一个空对象。完整字段见 `README_CN.md`(中文)/ `README.md`(English)。
262
+
263
+ ---
264
+
265
+ ## 六、常见问题
266
+
267
+ **`xfm: command not found`**
268
+ 全局 npm 的 `bin` 目录不在 `PATH` 里。执行 `npm bin -g`(旧版)或 `npm config get prefix`,把其下的 `bin` 加进 `PATH`。用 nvm 的话重开一个终端通常就能解决。
269
+
270
+ **安装时报 `EACCES: permission denied`**
271
+ 见「方式 A」里的全局前缀改法,不要用 `sudo`。
272
+
273
+ **`node -v` 低于 18**
274
+ 升级 Node 后重装:`npm i -g xfmpress --force`。
275
+
276
+ **`xfm new` 建了文章但页面上没有**
277
+ 大概率是 `draft: true`。改成 `false` 后重新 `xfm build`。
278
+
279
+ **Windows 下 `xfm` 提示不是内部或外部命令**
280
+ 用 Git Bash 或 PowerShell 重开终端;仍不行则检查 `%APPDATA%\npm` 是否在 `Path` 中。
281
+
282
+ **想升级**
283
+ ```bash
284
+ npm i -g xfmpress@latest # 全局
285
+ npm i -D xfmpress@latest # 项目内
286
+ ```
287
+
288
+ ---
289
+
290
+ ## 七、一键脚本(复制即用)
291
+
292
+ ```bash
293
+ # 全局安装 + 创建博客 + 本地预览
294
+ npm i -g xfmpress \
295
+ && xfm create my-blog --template=blog \
296
+ && cd my-blog \
297
+ && xfm dev
298
+ ```
package/README.md CHANGED
@@ -1,140 +1,144 @@
1
1
  # ⚡ xfm
2
2
 
3
- **A zero-dependency static site generator for Node.js.** 极致性能的静态站点生成器 —— 零运行时依赖、多线程渲染、Nunjucks 风格模板、即插即用的主题与插件。
3
+ **A zero-dependency static site generator for Node.js.** Zero runtime dependencies, multi-threaded rendering, Nunjucks-style templates, drop-in themes and plugins.
4
4
 
5
- ## 安装与上手
5
+ [English](./README.md) | [简体中文](./README_CN.md)
6
+
7
+ ## Install & Quick Start
6
8
 
7
9
  ```bash
8
- npm i -g xfmpress # 全局安装(Node ≥ 18),装完命令是 xfm
9
- xfm create my-blog # 30 秒创建站点
10
- cd my-blog && xfm dev # 毫秒级热更新
11
- xfm build # 生产构建
10
+ npm i -g xfmpress # install globally (Node >= 18); the command is xfm
11
+ xfm create my-blog # scaffold a site in 30 seconds
12
+ cd my-blog && xfm dev # instant hot reload
13
+ xfm build # production build
12
14
  ```
13
15
 
14
- > **包名 `xfmpress`,命令 `xfm`**(`npm i -g xfmpress` → 用 `xfm` 调用)。定位是 **Static Site Generator(静态站点生成器)**:把 Markdown 压成纯静态 HTML。
15
- > 与同系列的 [hexo-theme-xfm](https://github.com/xfm0797/hexo-theme-xfm) 共用 `xfm` 品牌。
16
- > 完整的三种安装方式、权限排错与 FAQ 见 **[INSTALL.md](./INSTALL.md)**。
16
+ > **Package name `xfmpress`, command `xfm`** (`npm i -g xfmpress`, then call `xfm`).
17
+ > This is a **static site generator**: it compresses Markdown into plain static HTML.
18
+ > Shares the `xfm` brand with [hexo-theme-xfm](https://github.com/xfm0797/hexo-theme-xfm).
19
+ > All three install methods, permission troubleshooting and the FAQ live in **[INSTALL.md](./INSTALL.md)**.
17
20
 
18
- ## 性能
21
+ ## Performance
19
22
 
20
- 压测环境:32 核 Node 22,1000 篇中文文档(约 5MB HTML 产出)。运行 `npm run bench` 可复现。
23
+ Benchmark: 32-core machine, Node 22, 1,000 Chinese documents (~5 MB of HTML output). Reproduce with `npm run bench`.
21
24
 
22
- | 指标 | 数值 |
25
+ | Metric | Value |
23
26
  | --- | --- |
24
- | 全量冷构建 | **≈0.38–0.42 s**(≈2400–2700 页/秒) |
25
- | 增量缓存命中后渲染阶段 | **1 ms**(1001 页) |
26
- | dev 热更新单页重建 | **2–7 ms** |
27
- | 产物运行时 | 纯静态 HTML + CSS,**零 JavaScript 依赖** |
27
+ | Full cold build | **≈0.38–0.42 s** (≈2,400–2,700 pages/s) |
28
+ | Render phase with warm cache | **1 ms** (1,001 pages) |
29
+ | Dev hot rebuild of a single page | **2–7 ms** |
30
+ | Runtime footprint | plain static HTML + CSS, **zero JavaScript** |
28
31
 
29
- 分阶段耗时(1000 页):
32
+ Per-stage timing (1,000 pages):
30
33
 
31
- | 阶段 | 耗时 | 优化手段 |
34
+ | Stage | Time | How |
32
35
  | --- | --- | --- |
33
- | 扫描与解析 | ~128 ms | 目录并行遍历 + 源文件并行读取,raw 只读一次 |
34
- | 哈希 | ~9 ms | 复用已读入的源文本,无二次 IO |
35
- | 渲染 | ~91 ms | worker 池复用线程,上下文只传一次 |
36
- | 写盘 | ~132 ms | 同目录只 mkdir 一次,两阶段并行写 |
36
+ | Scan & parse | ~128 ms | Parallel directory walk + parallel source reads; each file is read once |
37
+ | Hashing | ~9 ms | Reuses the already-read source text, no second IO |
38
+ | Render | ~91 ms | Worker pool reuses threads; site context is serialized once |
39
+ | Write | ~132 ms | One `mkdir` per directory, two-phase parallel writes |
37
40
 
38
- 性能来自四层设计:
41
+ Four design decisions carry the performance:
39
42
 
40
- 1. **线性扫描的 Markdown 引擎**:块级逐行推进 + 一次性行内正则,不构造 AST;内置中英文锚点、表格、任务列表(含续行与嵌套)、代码高亮、`::: callout` 容器。
41
- 2. **模板编译成函数**:Nunjucks 风格语法在构建期编译为 JS 函数,渲染只做字符串拼接,未定义变量安全返回 undefined。
42
- 3. **worker 池**:线程与模板编译缓存全程复用,站点上下文只序列化一次;并发度按"每线程最少工作量"标定(实测 2–4 个最优,堆到 8 个反而更慢)。
43
- 4. **增量构建**:`源文件哈希 + 主题指纹` 判定,未变页面直接复用缓存 HTML。
43
+ 1. **Line-scanning Markdown engine**: advances line by line at block level plus a single inline regex pass, no AST. Ships CJK and English anchors, tables, task lists (with continuation lines and nesting), syntax highlighting and `::: callout` containers.
44
+ 2. **Templates compiled to functions**: Nunjucks-style syntax is compiled into plain JS functions at build time, so rendering is string concatenation; undefined variables resolve safely to `undefined`.
45
+ 3. **Worker pool**: threads and template compile caches are reused for the whole run, and the site context is serialized only once. Concurrency is calibrated by *minimum work per thread* — 2–4 threads measured fastest; forcing 8 is actually slower.
46
+ 4. **Incremental builds**: a page is reused from cache when `source hash + theme fingerprint` are unchanged.
44
47
 
45
- > 并发度不是越多越好:worker 的启动与上下文克隆成本约 30–60 ms/个,
46
- > 在小任务量下会吃掉并行收益。`pickConcurrency()` 依据实测数据标定,而非按 CPU 核数堆线程。
48
+ > More workers is not better: spinning up a worker and cloning the context costs ~30–60 ms each,
49
+ > which eats the parallel gain on small workloads. `pickConcurrency()` calibrates from measurements
50
+ > instead of blindly matching CPU core count.
47
51
 
48
- ## 特性
52
+ ## Features
49
53
 
50
- - **主题**:`{% extend %}` / `{% block %}` 模板继承、`{% include %}` 片段、CSS 变量深浅色主题;内置 `default`(博客)与 `docs`(侧栏文档)两套主题。
51
- - **插件**:五个钩子(`extendContext` / `markdown` / `transformHtml` / `afterBuild` / `configureServer`),放进 `plugins/` 目录自动加载;内置 sitemap、RSS、阅读统计三个插件。
52
- - **内容**:YAML front-matter、草稿、标签/分类/系列聚合、自动 prev/next、按 weight 排序、`.md` 相对链接自动解析。
53
- - **开发体验**:`xfm dev` 内存构建 + SSE 热更新(保持滚动位置)、CSS 单独热替换、404 页列出可用路由。
54
- - **易迁移**:模板语法兼容 Nunjucks/Liquid 习惯,配置文件支持 `.mjs/.js/.json`。
54
+ - **Themes**: `{% extend %}` / `{% block %}` template inheritance, `{% include %}` partials, CSS-variable light/dark themes. Ships `default` (blog) and `docs` (sidebar documentation).
55
+ - **Plugins**: five hooks (`extendContext` / `markdown` / `transformHtml` / `afterBuild` / `configureServer`); drop a file into `plugins/` and it loads itself. Ships sitemap, RSS and reading-stats.
56
+ - **Content**: YAML front-matter, drafts, tag/category/series taxonomies, automatic prev/next, `weight` ordering, and relative `.md` links resolved automatically.
57
+ - **Developer experience**: `xfm dev` builds in memory with SSE hot reload (keeps your scroll position), hot-swaps CSS separately, and lists available routes on 404.
58
+ - **Easy to migrate**: template syntax follows Nunjucks/Liquid conventions; config supports `.mjs/.js/.json`.
55
59
 
56
- ## 安装
60
+ ## Install
57
61
 
58
62
  ```bash
59
- npm i -g xfmpress # 全局安装,然后直接使用 xfm 命令(Node ≥ 18)
60
- # 或免安装试用:npx xfmpress --help
63
+ npm i -g xfmpress # then use the xfm command (Node >= 18)
64
+ # or try without installing: npx xfmpress --help
61
65
  ```
62
66
 
63
- > 包名是 `xfmpress`,命令是 `xfm`,装一次即可全局使用。
64
- > 完整的安装、权限排错与三种安装方式见 **[INSTALL.md](./INSTALL.md)**。
67
+ > The package is `xfmpress`, the command is `xfm` — install once, use anywhere.
68
+ > Full install guide, permission troubleshooting and three install methods: **[INSTALL.md](./INSTALL.md)**.
65
69
 
66
- ## 命令
70
+ ## Commands
67
71
 
68
- | 命令 | 说明 |
72
+ | Command | Description |
69
73
  | --- | --- |
70
- | `xfm create [目录]` | 创建站点(`--template=blog\|docs`,`--title=`) |
71
- | `xfm dev [目录]` | 开发服务器(`--port=`) |
72
- | `xfm build [目录]` | 生产构建(`--dev` 启用增量缓存) |
73
- | `xfm new "标题"` | 新建文章(`--dir=posts --slug=xxx`) |
74
- | `xfm clean` / `xfm info` | 清理产物 / 查看配置 |
74
+ | `xfm create [dir]` | Scaffold a site (`--template=blog\|docs`, `--title=`) |
75
+ | `xfm dev [dir]` | Dev server (`--port=`) |
76
+ | `xfm build [dir]` | Production build (`--dev` enables incremental cache) |
77
+ | `xfm new "Title"` | New post (`--dir=posts --slug=xxx --draft`) |
78
+ | `xfm clean` / `xfm info` | Clean output / show config |
75
79
 
76
- ## 配置示例(xfm.config.mjs)
80
+ ## Configuration (`xfm.config.mjs`)
77
81
 
78
82
  ```js
79
83
  export default {
80
84
  site: {
81
85
  title: 'XFM Notes',
82
- description: '关于静态站点与前端的笔记',
83
- lang: 'zh-CN',
86
+ description: 'Notes on static sites and the frontend',
87
+ lang: 'en',
84
88
  base: '/',
85
- url: 'https://example.com', // 供 sitemap/RSS 使用
86
- nav: [{ title: '文章', url: '/' }],
89
+ url: 'https://example.com', // used by sitemap/RSS
90
+ nav: [{ title: 'Posts', url: '/' }],
87
91
  },
88
- theme: 'default', // 主题名或路径
89
- plugins: ['sitemap', 'rss'], // 内置插件,也可写路径
92
+ theme: 'default', // theme name or path
93
+ plugins: ['sitemap', 'rss'], // built-in names or paths
90
94
  defaults: { layout: 'post.html' },
91
- workers: 0, // worker 并发数;0=自动按任务量标定
92
- server: { port: 4321 },
95
+ workers: 0, // worker count; 0 = auto-calibrated
96
+ server: { port: 4173 },
93
97
  };
94
98
  ```
95
99
 
96
- ## 写作(content/ 目录)
100
+ ## Writing (`content/`)
97
101
 
98
102
  ```markdown
99
103
  ---
100
- title: 文章标题
104
+ title: Post title
101
105
  date: 2026-10-28 09:00:00
102
- tags: [Node.js, 性能]
103
- description: 摘要(列表页与 RSS 使用)
106
+ tags: [nodejs, performance]
107
+ description: Summary (used by list pages and RSS)
104
108
  draft: false
105
109
  ---
106
110
 
107
- 正文支持 GFM 全部常用语法,另加:
111
+ Body supports the common GFM syntax, plus:
108
112
 
109
- ::: tip 提示容器
110
- 带标题的高亮提示框。
113
+ ::: tip Callout
114
+ A highlighted box with a title.
111
115
  :::
112
116
 
113
117
  \`\`\`js
114
- const ok = true; // 内置语法高亮
118
+ const ok = true; // built-in syntax highlighting
115
119
  \`\`\`
116
120
 
117
- 代码围栏语言标记(含别名归一,大小写不敏感):`js / javascript / jsx / ts / tsx / java / c / cpp / csharp / go / golang / rust / rs / swift / kotlin / scala / php / python / py / bash / sh / shell / json / yaml / yml / html / xml / svg / vue / css / scss / markdown / md / text`。未知语言降级为纯转义文本,不会报错。
121
+ Fence languages (aliases normalized, case-insensitive): `js / javascript / jsx / ts / tsx / java / c / cpp / csharp / go / golang / rust / rs / swift / kotlin / scala / php / python / py / bash / sh / shell / json / yaml / yml / html / xml / svg / vue / css / scss / markdown / md / text`. Unknown languages fall back to escaped plain text instead of failing.
118
122
 
119
- 围栏首行可附加标题:\`\`\`js app.js(元信息如 `{1,3-5}` 会被忽略)。
123
+ A fence can carry a title on its first line: \`\`\`js app.js (meta such as `{1,3-5}` is ignored).
120
124
  ```
121
125
 
122
- `group: 使用指南` + `weight: 1` 字段可在 docs 主题生成侧栏导航。
126
+ In the `docs` theme, `group: Getting Started` plus `weight: 1` builds the sidebar navigation.
123
127
 
124
- ## 钩子语义
128
+ ## Hook semantics
125
129
 
126
- | 钩子 | 参数 | 返回值处理 |
130
+ | Hook | Arguments | Return value |
127
131
  | --- | --- | --- |
128
- | `extendContext(ctx)` | 站点上下文 | 替换上下文(返回值写入 `site.*`) |
129
- | `markdown(src, page)` | 当前源码 | 替换源码 |
130
- | `transformHtml(html, ctx)` | 当前 HTML | 替换 HTML |
131
- | `afterBuild(site)` | 站点实例(`site.documents[].html` 为**正文** HTML,不含布局) | 返回的 `[{path, contents}]` 被汇总写出 |
132
- | `configureServer(server)` / `finish(site)` | 站点/服务器 | 忽略 |
132
+ | `extendContext(ctx)` | Site context | Replaces the context (merged into `site.*`) |
133
+ | `markdown(src, page)` | Current source | Replaces the source |
134
+ | `transformHtml(html, ctx)` | Current HTML | Replaces the HTML |
135
+ | `afterBuild(site)` | Site instance (`site.documents[].html` is **body** HTML, without layout) | Returned `[{path, contents}]` are written out |
136
+ | `configureServer(server)` / `finish(site)` | Site / server | Ignored |
133
137
 
134
- ## 插件示例
138
+ ## Plugin example
135
139
 
136
140
  ```js
137
- // plugins/search-index.js —— 放进 plugins/ 目录即生效
141
+ // plugins/search-index.js — drop into plugins/ and it runs
138
142
  module.exports = () => ({
139
143
  name: 'search-index',
140
144
  afterBuild(site) {
@@ -147,39 +151,39 @@ module.exports = () => ({
147
151
  });
148
152
  ```
149
153
 
150
- ## 主题结构
154
+ ## Theme structure
151
155
 
152
156
  ```
153
157
  themes/my-theme/
154
- ├── layouts/ # base.html(骨架)、index/post/page.html(继承 base)
155
- ├── partials/ # 可复用片段(toc、卡片等)
156
- └── assets/ # style.css 等,构建时复制到站点 /assets/
158
+ ├── layouts/ # base.html (skeleton), index/post/page.html (extend base)
159
+ ├── partials/ # reusable fragments (toc, cards, ...)
160
+ └── assets/ # style.css and friends, copied to /assets/ on build
157
161
  ```
158
162
 
159
- 模板上下文:`site.*`(站点配置与聚合数据)、`page.*`(当前页元数据)、`content`(正文 HTML)、`headings`(目录)。
163
+ Template context: `site.*` (site config and aggregated data), `page.*` (current page metadata), `content` (body HTML), `headings` (table of contents).
160
164
 
161
- ## 目录
165
+ ## Project layout
162
166
 
163
167
  ```
164
168
  xfm/
165
- ├── bin/xfm.js # CLI(build/dev/new/create/clean/info)
169
+ ├── bin/xfm.js # CLI (build/dev/new/create/clean/info)
166
170
  ├── lib/engine/ # markdown / template / render / frontmatter / theme / plugins / site / worker / server / config
167
- ├── lib/plugins/ # 内置插件(sitemap / rss / reading-stats)
168
- ├── lib/cli/scaffold.js # create 脚手架
169
- ├── themes/default/ # 博客主题(深浅色)
170
- ├── themes/docs/ # 文档主题(侧栏导航)
171
- ├── examples/blog/ # 可构建的示例站点
172
- ├── bench/bench.js # 基准测试(分阶段耗时)
173
- └── test/run.js # 72 项测试(零依赖)
171
+ ├── lib/plugins/ # built-in plugins (sitemap / rss / reading-stats)
172
+ ├── lib/cli/scaffold.js # create scaffolding
173
+ ├── themes/default/ # blog theme (light/dark)
174
+ ├── themes/docs/ # docs theme (sidebar navigation)
175
+ ├── examples/blog/ # buildable example site
176
+ ├── bench/bench.js # benchmark (per-stage timings)
177
+ └── test/run.js # 72 tests (zero dependencies)
174
178
  ```
175
179
 
176
- ## 本地开发
180
+ ## Local development
177
181
 
178
182
  ```bash
179
- npm run test # 72 项测试
180
- npm run bench # 1000 页基准(分阶段耗时)
181
- npm run build:example # 构建示例站点
182
- npm run dev # 开发示例站点
183
+ npm run test # 72 tests
184
+ npm run bench # 1,000-page benchmark (per-stage timings)
185
+ npm run build:example # build the example site
186
+ npm run dev # dev-server the example site
183
187
  ```
184
188
 
185
189
  MIT License
package/README_CN.md ADDED
@@ -0,0 +1,187 @@
1
+ # ⚡ xfm
2
+
3
+ **A zero-dependency static site generator for Node.js.** 极致性能的静态站点生成器 —— 零运行时依赖、多线程渲染、Nunjucks 风格模板、即插即用的主题与插件。
4
+
5
+ [English](./README.md) | 简体中文
6
+
7
+ ## 安装与上手
8
+
9
+ ```bash
10
+ npm i -g xfmpress # 全局安装(Node ≥ 18),装完命令是 xfm
11
+ xfm create my-blog # 30 秒创建站点
12
+ cd my-blog && xfm dev # 毫秒级热更新
13
+ xfm build # 生产构建
14
+ ```
15
+
16
+ > **包名 `xfmpress`,命令 `xfm`**(`npm i -g xfmpress` → 用 `xfm` 调用)。定位是 **Static Site Generator(静态站点生成器)**:把 Markdown 压成纯静态 HTML。
17
+ > 与同系列的 [hexo-theme-xfm](https://github.com/xfm0797/hexo-theme-xfm) 共用 `xfm` 品牌。
18
+ > 完整的三种安装方式、权限排错与 FAQ 见 **[INSTALL_CN.md](./INSTALL_CN.md)**。
19
+
20
+ ## 性能
21
+
22
+ 压测环境:32 核 Node 22,1000 篇中文文档(约 5MB HTML 产出)。运行 `npm run bench` 可复现。
23
+
24
+ | 指标 | 数值 |
25
+ | --- | --- |
26
+ | 全量冷构建 | **≈0.38–0.42 s**(≈2400–2700 页/秒) |
27
+ | 增量缓存命中后渲染阶段 | **1 ms**(1001 页) |
28
+ | dev 热更新单页重建 | **2–7 ms** |
29
+ | 产物运行时 | 纯静态 HTML + CSS,**零 JavaScript 依赖** |
30
+
31
+ 分阶段耗时(1000 页):
32
+
33
+ | 阶段 | 耗时 | 优化手段 |
34
+ | --- | --- | --- |
35
+ | 扫描与解析 | ~128 ms | 目录并行遍历 + 源文件并行读取,raw 只读一次 |
36
+ | 哈希 | ~9 ms | 复用已读入的源文本,无二次 IO |
37
+ | 渲染 | ~91 ms | worker 池复用线程,上下文只传一次 |
38
+ | 写盘 | ~132 ms | 同目录只 mkdir 一次,两阶段并行写 |
39
+
40
+ 性能来自四层设计:
41
+
42
+ 1. **线性扫描的 Markdown 引擎**:块级逐行推进 + 一次性行内正则,不构造 AST;内置中英文锚点、表格、任务列表(含续行与嵌套)、代码高亮、`::: callout` 容器。
43
+ 2. **模板编译成函数**:Nunjucks 风格语法在构建期编译为 JS 函数,渲染只做字符串拼接,未定义变量安全返回 undefined。
44
+ 3. **worker 池**:线程与模板编译缓存全程复用,站点上下文只序列化一次;并发度按"每线程最少工作量"标定(实测 2–4 个最优,堆到 8 个反而更慢)。
45
+ 4. **增量构建**:`源文件哈希 + 主题指纹` 判定,未变页面直接复用缓存 HTML。
46
+
47
+ > 并发度不是越多越好:worker 的启动与上下文克隆成本约 30–60 ms/个,
48
+ > 在小任务量下会吃掉并行收益。`pickConcurrency()` 依据实测数据标定,而非按 CPU 核数堆线程。
49
+
50
+ ## 特性
51
+
52
+ - **主题**:`{% extend %}` / `{% block %}` 模板继承、`{% include %}` 片段、CSS 变量深浅色主题;内置 `default`(博客)与 `docs`(侧栏文档)两套主题。
53
+ - **插件**:五个钩子(`extendContext` / `markdown` / `transformHtml` / `afterBuild` / `configureServer`),放进 `plugins/` 目录自动加载;内置 sitemap、RSS、阅读统计三个插件。
54
+ - **内容**:YAML front-matter、草稿、标签/分类/系列聚合、自动 prev/next、按 weight 排序、`.md` 相对链接自动解析。
55
+ - **开发体验**:`xfm dev` 内存构建 + SSE 热更新(保持滚动位置)、CSS 单独热替换、404 页列出可用路由。
56
+ - **易迁移**:模板语法兼容 Nunjucks/Liquid 习惯,配置文件支持 `.mjs/.js/.json`。
57
+
58
+ ## 安装
59
+
60
+ ```bash
61
+ npm i -g xfmpress # 全局安装,然后直接使用 xfm 命令(Node ≥ 18)
62
+ # 或免安装试用:npx xfmpress --help
63
+ ```
64
+
65
+ > 包名是 `xfmpress`,命令是 `xfm`,装一次即可全局使用。
66
+ > 完整的安装、权限排错与三种安装方式见 **[INSTALL_CN.md](./INSTALL_CN.md)**。
67
+
68
+ ## 命令
69
+
70
+ | 命令 | 说明 |
71
+ | --- | --- |
72
+ | `xfm create [目录]` | 创建站点(`--template=blog\|docs`,`--title=`) |
73
+ | `xfm dev [目录]` | 开发服务器(`--port=`) |
74
+ | `xfm build [目录]` | 生产构建(`--dev` 启用增量缓存) |
75
+ | `xfm new "标题"` | 新建文章(`--dir=posts --slug=xxx`) |
76
+ | `xfm clean` / `xfm info` | 清理产物 / 查看配置 |
77
+
78
+ ## 配置示例(xfm.config.mjs)
79
+
80
+ ```js
81
+ export default {
82
+ site: {
83
+ title: 'XFM Notes',
84
+ description: '关于静态站点与前端的笔记',
85
+ lang: 'zh-CN',
86
+ base: '/',
87
+ url: 'https://example.com', // 供 sitemap/RSS 使用
88
+ nav: [{ title: '文章', url: '/' }],
89
+ },
90
+ theme: 'default', // 主题名或路径
91
+ plugins: ['sitemap', 'rss'], // 内置插件,也可写路径
92
+ defaults: { layout: 'post.html' },
93
+ workers: 0, // worker 并发数;0=自动按任务量标定
94
+ server: { port: 4173 },
95
+ };
96
+ ```
97
+
98
+ ## 写作(content/ 目录)
99
+
100
+ ```markdown
101
+ ---
102
+ title: 文章标题
103
+ date: 2026-10-28 09:00:00
104
+ tags: [Node.js, 性能]
105
+ description: 摘要(列表页与 RSS 使用)
106
+ draft: false
107
+ ---
108
+
109
+ 正文支持 GFM 全部常用语法,另加:
110
+
111
+ ::: tip 提示容器
112
+ 带标题的高亮提示框。
113
+ :::
114
+
115
+ \`\`\`js
116
+ const ok = true; // 内置语法高亮
117
+ \`\`\`
118
+
119
+ 代码围栏语言标记(含别名归一,大小写不敏感):`js / javascript / jsx / ts / tsx / java / c / cpp / csharp / go / golang / rust / rs / swift / kotlin / scala / php / python / py / bash / sh / shell / json / yaml / yml / html / xml / svg / vue / css / scss / markdown / md / text`。未知语言降级为纯转义文本,不会报错。
120
+
121
+ 围栏首行可附加标题:\`\`\`js app.js(元信息如 `{1,3-5}` 会被忽略)。
122
+ ```
123
+
124
+ `group: 使用指南` + `weight: 1` 字段可在 docs 主题生成侧栏导航。
125
+
126
+ ## 钩子语义
127
+
128
+ | 钩子 | 参数 | 返回值处理 |
129
+ | --- | --- | --- |
130
+ | `extendContext(ctx)` | 站点上下文 | 替换上下文(返回值写入 `site.*`) |
131
+ | `markdown(src, page)` | 当前源码 | 替换源码 |
132
+ | `transformHtml(html, ctx)` | 当前 HTML | 替换 HTML |
133
+ | `afterBuild(site)` | 站点实例(`site.documents[].html` 为**正文** HTML,不含布局) | 返回的 `[{path, contents}]` 被汇总写出 |
134
+ | `configureServer(server)` / `finish(site)` | 站点/服务器 | 忽略 |
135
+
136
+ ## 插件示例
137
+
138
+ ```js
139
+ // plugins/search-index.js —— 放进 plugins/ 目录即生效
140
+ module.exports = () => ({
141
+ name: 'search-index',
142
+ afterBuild(site) {
143
+ const index = site.documents.map(d => ({
144
+ title: d.title, url: d.url,
145
+ text: d.html.replace(/<[^>]+>/g, ' ').slice(0, 800),
146
+ }));
147
+ return [{ path: 'search.json', contents: JSON.stringify(index) }];
148
+ },
149
+ });
150
+ ```
151
+
152
+ ## 主题结构
153
+
154
+ ```
155
+ themes/my-theme/
156
+ ├── layouts/ # base.html(骨架)、index/post/page.html(继承 base)
157
+ ├── partials/ # 可复用片段(toc、卡片等)
158
+ └── assets/ # style.css 等,构建时复制到站点 /assets/
159
+ ```
160
+
161
+ 模板上下文:`site.*`(站点配置与聚合数据)、`page.*`(当前页元数据)、`content`(正文 HTML)、`headings`(目录)。
162
+
163
+ ## 目录
164
+
165
+ ```
166
+ xfm/
167
+ ├── bin/xfm.js # CLI(build/dev/new/create/clean/info)
168
+ ├── lib/engine/ # markdown / template / render / frontmatter / theme / plugins / site / worker / server / config
169
+ ├── lib/plugins/ # 内置插件(sitemap / rss / reading-stats)
170
+ ├── lib/cli/scaffold.js # create 脚手架
171
+ ├── themes/default/ # 博客主题(深浅色)
172
+ ├── themes/docs/ # 文档主题(侧栏导航)
173
+ ├── examples/blog/ # 可构建的示例站点
174
+ ├── bench/bench.js # 基准测试(分阶段耗时)
175
+ └── test/run.js # 72 项测试(零依赖)
176
+ ```
177
+
178
+ ## 本地开发
179
+
180
+ ```bash
181
+ npm run test # 72 项测试
182
+ npm run bench # 1000 页基准(分阶段耗时)
183
+ npm run build:example # 构建示例站点
184
+ npm run dev # 开发示例站点
185
+ ```
186
+
187
+ MIT License
package/bin/xfm.js CHANGED
@@ -253,7 +253,7 @@ ${paint(C.bold, '命令')}
253
253
  info [目录] 查看站点配置
254
254
 
255
255
  ${paint(C.bold, '选项')}
256
- --port=4000 dev 端口
256
+ --port=4173 dev 端口(默认 4173)
257
257
  --dev build 时启用增量缓存
258
258
  --template=docs create 时使用文档模板
259
259
  --draft new 时生成草稿(默认直接可发布)
@@ -43,7 +43,7 @@ async function scaffold(args) {
43
43
  theme: '${template === 'docs' ? 'docs' : 'default'}',
44
44
  plugins: ['sitemap', 'rss'],
45
45
  defaults: { layout: '${layout}' },
46
- server: { port: 4321 },
46
+ server: { port: 4173 },
47
47
  };
48
48
  `
49
49
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "xfmpress",
3
- "version": "0.1.0",
4
- "description": "Zero-dependency static site generator for Node.js — 极致性能、多线程渲染、主题与插件系统",
3
+ "version": "0.1.1",
4
+ "description": "Zero-dependency static site generator for Node.js — multi-threaded rendering, Nunjucks-style templates, drop-in themes and plugins",
5
5
  "bin": {
6
6
  "xfm": "bin/xfm.js"
7
7
  },
@@ -14,7 +14,10 @@
14
14
  "bin",
15
15
  "lib",
16
16
  "themes",
17
- "INSTALL.md"
17
+ "INSTALL.md",
18
+ "README_CN.md",
19
+ "INSTALL_CN.md",
20
+ "CHANGELOG.md"
18
21
  ],
19
22
  "scripts": {
20
23
  "test": "node test/run.js",