一个文档同步工具,用于从多个 Git 仓库或本地目录拉取内容并编译到本地项目中。
- 🔄 多源同步: 从多个 Git 仓库拉取文档
- 📁 本地源支持: 直接使用本地目录作为文档源(无需拷贝,实时反映变化)
- 📝 包含系统: 使用
@include指令引用和包含已同步源的内容 - 🎯 站点过滤: 根据站点配置有条件地包含/排除内容
- 📊 依赖追踪: 使用依赖图进行增量编译
- 👀 监视模式: 当源文件更改时自动重新编译(支持本地源监视)
- 🔍 预演模式: 预览更改而不写入文件
- ⚙️ 灵活配置: 支持自定义配置文件路径
npm install
npm run build在项目根目录创建配置文件,支持 TypeScript 或 JavaScript 格式:
创建 docs-sync.config.ts:
import { defineConfig } from 'docs-sync-cli';
export default defineConfig({
site: 'site-a',
cacheDir: '.docs-sync-cache',
sources: {
// Git 仓库源(必须指定 branch)
common: {
repo: 'git@github.com:your-org/docs-common.git',
branch: 'main',
},
// 本地目录源(支持绝对路径和相对路径,branch 可选)
local: {
repo: './source', // 相对路径,相对于 cwd
},
// 本地绝对路径源
other: {
repo: '/Users/username/my-docs',
},
},
mappings: [
// 从 Git 仓库同步
{
from: 'common:guides/install.md',
to: 'docs/guide/install.md',
},
// 从本地目录同步
{
from: 'local:README.md',
to: 'docs/README.md',
},
],
});创建 docs-sync.config.js:
const { defineConfig } = require('docs-sync-cli');
module.exports = defineConfig({
site: 'site-a',
cacheDir: '.docs-sync-cache',
sources: {
common: {
repo: 'git@github.com:your-org/docs-common.git',
branch: 'main',
},
},
mappings: [
{
from: 'common:guides/install.md',
to: 'docs/guide/install.md',
},
],
});注意: CLI 会自动查找配置文件,优先级为
docs-sync.config.ts>docs-sync.config.js
每个源可以是:
-
Git 仓库:
- 提供 Git URL (如
git@github.com:...或https://github.com/...) - 必须指定
branch字段
- 提供 Git URL (如
-
本地目录:
- 提供本地路径 (绝对路径如
/path/to/docs或相对路径如./source) branch字段可选,会被忽略
- 提供本地路径 (绝对路径如
本地源的优势:
- ✅ 无需拷贝,直接使用绝对路径读取
- ✅ 节省磁盘空间
- ✅ 实时反映文件变化
- ✅ 更适合开发调试场景
- ✅ 不需要配置
branch字段
定义从源到目标的文件映射关系:
from: 格式为source-key:path/to/file.mdto: 相对于当前工作目录的目标路径
# 同步一次
docs-sync sync
# 监视模式(自动重新编译变更的文件)
docs-sync watch# 使用自定义配置文件
docs-sync sync --config path/to/config.ts
docs-sync sync -c custom.config.ts
# 预演模式(预览更改,不写入文件)
docs-sync sync --dry-run
# 调试模式(显示详细日志)
docs-sync sync --debug
# 组合使用
docs-sync sync -c custom.config.ts --dry-run --debug可用选项:
-c, --config <path>: 指定配置文件路径(默认:docs-sync.config.ts从当前目录)--dry-run: 预演模式,只显示将要做的更改,不实际写入文件--debug: 启用调试日志,显示详细信息(如内容哈希、依赖关系等)
{
"scripts": {
"sync": "docs-sync sync",
"sync:dry": "docs-sync sync --dry-run",
"sync:debug": "docs-sync sync --debug",
"watch": "docs-sync watch"
}
}然后运行:
npm run sync
npm run sync:dry
npm run watch从已同步的源包含内容:
<!-- @include common:snippets/warning.md -->嵌套支持:
@include 指令支持递归嵌套。被包含的文件中也可以包含 @include 指令,系统会自动递归处理。
示例:
common:snippets/warning.md:
> **⚠️ 警告**: 请仔细阅读以下内容
<!-- @include common:snippets/note.md -->common:snippets/note.md:
**注意**: 这是一个重要提示结果:
> **⚠️ 警告**: 请仔细阅读以下内容
**注意**: 这是一个重要提示多次引用 vs 循环引用:
-
✅ 多次引用(合法): 同一个文件可以在不同的位置被多次包含
<!-- @include common:note.md --> Some content <!-- @include common:note.md --> <!-- 合法,允许重复引用 -->
-
❌ 循环引用(非法): 形成 A→B→A 的引用链
// a.md: <!-- @include common:b.md --> // b.md: <!-- @include common:a.md --> <!-- 非法,形成循环 -->
系统使用调用栈检测循环引用,只在当前包含链中检查重复。如果检测到循环引用,会在生成的文档中插入错误提示:
<!-- ERROR: Circular include detected: common:circular.md -->根据站点有条件地包含内容:
<!-- @site site-a -->
此内容仅出现在 site-a
<!-- @endsite -->
<!-- @site site-b, site-c -->
此内容仅出现在 site-b 和 site-c
<!-- @endsite -->
<!-- @site !site-a -->
此内容出现在除 site-a 外的所有站点
<!-- @endsite -->docs-sync/
├── package.json
├── tsconfig.json
├── bin/
│ └── docs-sync.ts # CLI 入口
├── src/
│ ├── config.ts # 配置加载器
│ ├── git.ts # Git 操作(clone/pull)
│ ├── compiler.ts # 文件编译
│ ├── include.ts # @include 指令
│ ├── site-filter.ts # @site 指令
│ ├── hash.ts # 内容哈希
│ ├── graph.ts # 依赖图
│ ├── engine.ts # sync/watch 命令
│ ├── logger.ts # 日志系统(带颜色和时间戳)
│ └── fs/
│ ├── types.ts # 文件系统接口
│ ├── realFs.ts # 真实文件系统操作
│ └── dryRunFs.ts # 预演文件系统(仅日志)
└── docs-sync.config.ts # 用户配置
-
准备源:
- Git 源: Clone/Pull 到缓存目录 (
.docs-sync-cache/source-key/) - 本地源: 解析为绝对路径,直接使用(无需拷贝)
- Git 源: Clone/Pull 到缓存目录 (
-
编译映射: 对于每个映射:
- 从源路径读取文件
- 处理
@include指令(递归包含其他文件) - 根据
@site标签过滤内容 - 生成内容哈希用于变更检测
- 写入目标位置
-
监视模式:
- 监视所有源的路径(包括本地源)
- 检测文件变更
- 使用依赖图找出受影响的映射
- 只重新编译受影响的文件
┌─────────────────────────────────────────────┐
│ docs-sync CLI │
│ (支持 CommonJS, 无 top-level await) │
└─────────────────┬───────────────────────────┘
│
┌─────────────┴─────────────┐
│ │
┌───▼────┐ ┌────▼────┐
│ Git 源 │ │ 本地源 │
│ (拉取) │ │ (直接) │
└───┬────┘ └────┬────┘
│ │
└─────────────┬─────────────┘
│
┌─────▼──────┐
│ sourcePaths│ (映射表)
└─────┬──────┘
│
┌────────┴────────┐
│ 编译器 │
│ - resolveIncludes│
│ - filterBySite │
│ - hashContent │
└────────┬─────────┘
│
┌─────▼──────┐
│ 目标文件 │
└────────────┘
关键改进:
- 本地源使用绝对路径,性能更好,实时反映变化
- 统一的
sourcePaths映射表,简化路径解析 - Watch 模式支持监视本地目录变更
你有一个共享的文档仓库,多个站点需要使用其中的内容:
export default defineConfig({
site: 'site-a',
sources: {
common: {
repo: 'git@github.com:company/docs-common.git',
branch: 'main',
},
},
mappings: [
// 共享的安装指南
{ from: 'common:guides/install.md', to: 'docs/guides/install.md' },
// 但每个站点有自己的配置说明
{ from: 'common:guides/config-site-a.md', to: 'docs/guides/config.md' },
],
});开发时使用本地源,生产环境使用 Git 源:
const isDev = process.env.NODE_ENV === 'development';
export default defineConfig({
site: 'site-a',
sources: {
common: isDev
? { repo: './local-docs', branch: 'main' } // 开发时用本地
: { repo: 'git@github.com:company/docs.git', branch: 'main' }, // 生产用 Git
},
mappings: [
{ from: 'common:README.md', to: 'docs/README.md' },
],
});使用 @include 和 @site 创建灵活的文档:
源文件 (common:snippets/warning.md):
> **⚠️ 警告**: 请仔细阅读以下内容源文件 (common:guides/install.md):
# 安装指南
<!-- @include common:snippets/warning.md -->
## 通用步骤
1. 下载安装包
2. 运行安装程序
<!-- @site site-a -->
## Site A 特定配置
Site A 需要额外配置数据库连接。
<!-- @endsite -->
<!-- @site site-b -->
## Site B 特定配置
Site B 使用云端配置,无需本地设置。
<!-- @endsite -->编译后的文档 (site-a):
# 安装指南
> **⚠️ 警告**: 请仔细阅读以下内容
## 通用步骤
1. 下载安装包
2. 运行安装程序
## Site A 特定配置
Site A 需要额外配置数据库连接。本地源:
- 直接读取本地文件,无需拷贝
- 实时反映文件变化
- 适合开发调试
- 性能更好
Git 源:
- 自动 clone/pull 到缓存目录
- 适合生产环境
- 可以指定分支
- 支持远程协作
使用 --debug 标志查看详细日志:
docs-sync sync --debug这会显示:
- 文件读取路径
- 内容哈希值
- 依赖关系
- Include 处理过程
- 依赖图可视化 - 以 ASCII 树形结构展示文件间的依赖关系
在 watch 模式下,每次文件变更后也会显示更新后的依赖图:
docs-sync watch --debug依赖图示例:
=== Dependency Graph ===
common:snippet.md
└─ docs1:main.md
└─ output/site-a/main.md
common:intro.md
└─ docs1:main.md
└─ output/site-a/main.md
- Git 源: 监视
.docs-sync-cache/source-key/目录 - 本地源: 监视本地源的绝对路径
当任何 .md 文件变化时,会自动重新编译受影响的映射。
当前版本使用 CommonJS:
- 编译目标:
target: ES2020,module: commonjs - 使用
ts-node/register加载 TypeScript 配置文件 - 兼容 Node.js 14+
MIT