growork 1.1.1 → 1.1.2
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/.claude/settings.local.json +2 -1
- package/README.md +55 -123
- package/claude.md +40 -10
- package/dist/index.js +32 -6
- package/docs/architecture.md +175 -0
- package/docs/developer-notes.md +67 -0
- package/docs/prd-1.1.2.md +136 -0
- package/growork.config.yaml +5 -0
- package/package.json +1 -1
- package/src/commands/sync.ts +36 -7
- package/src/utils/config.ts +5 -2
- package/tests/config.test.ts +13 -2
- package/tests/feishu.test.ts +187 -0
- package/tests/notion.test.ts +81 -0
- package/tests/sync.test.ts +65 -0
- /package/docs/{product/prd-1.0.0.md → prd-1.0.0.md} +0 -0
- /package/docs/{product/prd-1.1.0.md → prd-1.1.1.md} +0 -0
- /package/docs/{product/prd-template.md → prd-template.md} +0 -0
package/README.md
CHANGED
|
@@ -4,58 +4,40 @@
|
|
|
4
4
|
|
|
5
5
|
这样 AI 工具(如 Claude、Cursor、Windsurf)就能直接读取你的产品文档,帮你写代码。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/growork)
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 功能
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
- 自动下载飞书文档,转成 Markdown 格式
|
|
12
|
+
- 自动下载 Notion 页面,转成 Markdown 格式
|
|
13
|
+
- 支持 Figma 设计稿链接,自动生成链接文档
|
|
14
|
+
- 按**版本 → 功能 → 文档类型**组织文档,AI 更容易理解
|
|
15
|
+
- 一条命令同步所有文档
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
> ⚠️ **每个版本都是破坏性更新**,不保证向后兼容。升级前请查看更新日志,必要时重新初始化配置文件。
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## 安装
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
### 1. 安装 Node.js
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## 安装步骤
|
|
26
|
-
|
|
27
|
-
### 第一步:安装 Node.js
|
|
28
|
-
|
|
29
|
-
GroWork 需要 Node.js 才能运行。如果你还没安装:
|
|
23
|
+
GroWork 需要 Node.js >= 18。如果你还没安装:
|
|
30
24
|
|
|
31
25
|
1. 打开 https://nodejs.org/
|
|
32
26
|
2. 点击绿色的 **"LTS"** 按钮下载
|
|
33
27
|
3. 双击下载的文件,按提示完成安装
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
打开「终端」(Mac)或「命令提示符」(Windows),输入:
|
|
29
|
+
检查是否安装成功:
|
|
38
30
|
|
|
39
31
|
```bash
|
|
40
32
|
node -v
|
|
41
33
|
```
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
### 第二步:安装 GroWork
|
|
48
|
-
|
|
49
|
-
在终端中输入:
|
|
35
|
+
### 2. 安装 GroWork
|
|
50
36
|
|
|
51
37
|
```bash
|
|
52
38
|
npm install -g growork
|
|
53
39
|
```
|
|
54
40
|
|
|
55
|
-
等待安装完成即可。
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
41
|
## 使用方法
|
|
60
42
|
|
|
61
43
|
### 1. 初始化项目
|
|
@@ -68,14 +50,10 @@ growork init
|
|
|
68
50
|
|
|
69
51
|
这会在当前目录创建一个 `growork.config.yaml` 配置文件。
|
|
70
52
|
|
|
71
|
-
---
|
|
72
|
-
|
|
73
53
|
### 2. 获取飞书凭证(如果你要同步飞书文档)
|
|
74
54
|
|
|
75
55
|
你需要在飞书开放平台创建一个应用,获取「App ID」和「App Secret」。
|
|
76
56
|
|
|
77
|
-
**详细步骤:**
|
|
78
|
-
|
|
79
57
|
1. **打开飞书开放平台**
|
|
80
58
|
- 国际版 Lark:https://open.larksuite.com/
|
|
81
59
|
- 国内版飞书:https://open.feishu.cn/
|
|
@@ -83,41 +61,26 @@ growork init
|
|
|
83
61
|
2. **创建应用**
|
|
84
62
|
- 点击「创建企业自建应用」
|
|
85
63
|
- 填写应用名称(随便起,比如「文档同步」)
|
|
86
|
-
- 点击「创建」
|
|
87
64
|
|
|
88
65
|
3. **获取凭证**
|
|
89
66
|
- 在应用详情页,找到「凭证与基础信息」
|
|
90
|
-
- 复制 **App ID**(以 `cli_`
|
|
91
|
-
- 复制 **App Secret**
|
|
67
|
+
- 复制 **App ID**(以 `cli_` 开头)和 **App Secret**
|
|
92
68
|
|
|
93
69
|
4. **开通权限**
|
|
94
70
|
- 点击左侧「权限管理」
|
|
95
|
-
-
|
|
96
|
-
- `docx:document:readonly`(读取文档)
|
|
97
|
-
- `wiki:wiki:readonly`(读取知识库)
|
|
98
|
-
- 点击「批量开通」
|
|
71
|
+
- 搜索并开通:`docx:document:readonly`、`wiki:wiki:readonly`
|
|
99
72
|
|
|
100
73
|
5. **发布应用**
|
|
101
|
-
-
|
|
102
|
-
- 点击「创建版本」,填写版本号(如 1.0.0)
|
|
103
|
-
- 提交审核(企业内部应用一般自动通过)
|
|
104
|
-
|
|
105
|
-
---
|
|
74
|
+
- 点击「版本管理与发布」→「创建版本」→ 提交审核
|
|
106
75
|
|
|
107
76
|
### 3. 获取 Notion 凭证(如果你要同步 Notion 页面)
|
|
108
77
|
|
|
109
|
-
**详细步骤:**
|
|
110
|
-
|
|
111
78
|
1. **创建 Integration**
|
|
112
79
|
- 打开 https://www.notion.so/my-integrations
|
|
113
|
-
- 点击「New integration
|
|
114
|
-
- 填写名称(随便起,比如「文档同步」)
|
|
115
|
-
- 选择你的 Workspace
|
|
116
|
-
- 点击「Submit」
|
|
80
|
+
- 点击「New integration」,填写名称,选择 Workspace
|
|
117
81
|
|
|
118
82
|
2. **复制 Token**
|
|
119
|
-
- 在 Integration
|
|
120
|
-
- 复制这个 Token(以 `ntn_` 或 `secret_` 开头)
|
|
83
|
+
- 在 Integration 详情页,复制 Token(以 `ntn_` 或 `secret_` 开头)
|
|
121
84
|
|
|
122
85
|
3. **给页面授权**(重要!)
|
|
123
86
|
- 打开你要同步的 Notion 页面
|
|
@@ -125,8 +88,6 @@ growork init
|
|
|
125
88
|
- 选择你刚创建的 Integration
|
|
126
89
|
- **每个要同步的页面都需要做这一步!**
|
|
127
90
|
|
|
128
|
-
---
|
|
129
|
-
|
|
130
91
|
### 4. 编辑配置文件
|
|
131
92
|
|
|
132
93
|
用任意文本编辑器打开 `growork.config.yaml`,填入你的凭证和文档链接:
|
|
@@ -157,8 +118,9 @@ versions:
|
|
|
157
118
|
用户登录: # 功能名称
|
|
158
119
|
prd: # PRD 文档
|
|
159
120
|
- "https://xxx.feishu.cn/docx/xxxxx"
|
|
160
|
-
design: #
|
|
161
|
-
- "https://
|
|
121
|
+
design: # 设计文档(支持 Figma 链接)
|
|
122
|
+
- "https://www.figma.com/design/xxx?node-id=1-20"
|
|
123
|
+
- "https://www.figma.com/design/xxx?node-id=1-30"
|
|
162
124
|
api: # 接口文档
|
|
163
125
|
- "https://xxx.feishu.cn/docx/zzzzz"
|
|
164
126
|
test: # 测试用例
|
|
@@ -171,26 +133,23 @@ versions:
|
|
|
171
133
|
|
|
172
134
|
**配置说明:**
|
|
173
135
|
|
|
174
|
-
| 区域
|
|
175
|
-
|
|
|
176
|
-
| `custom`
|
|
177
|
-
| `versions` | 按版本和功能组织的文档
|
|
178
|
-
| `prd`
|
|
179
|
-
| `design`
|
|
180
|
-
| `api`
|
|
181
|
-
| `test`
|
|
136
|
+
| 区域 | 说明 |
|
|
137
|
+
| ---------- | ------------------------------------------------ |
|
|
138
|
+
| `custom` | 全局文档,不属于任何版本(如技术架构、编码规范) |
|
|
139
|
+
| `versions` | 按版本和功能组织的文档 |
|
|
140
|
+
| `prd` | 产品需求文档 |
|
|
141
|
+
| `design` | 设计稿(支持 Figma 链接,自动生成链接文档) |
|
|
142
|
+
| `api` | 接口/技术方案 |
|
|
143
|
+
| `test` | 测试用例 |
|
|
182
144
|
|
|
183
145
|
**如何获取文档链接?**
|
|
184
146
|
|
|
185
147
|
- **飞书**:打开文档,直接复制浏览器地址栏的链接
|
|
186
148
|
- **Notion**:打开页面,点击右上角「Share」→「Copy link」
|
|
187
|
-
|
|
188
|
-
---
|
|
149
|
+
- **Figma**:打开设计稿,复制浏览器地址栏的链接(可带 `node-id` 参数定位到具体页面)
|
|
189
150
|
|
|
190
151
|
### 5. 同步文档
|
|
191
152
|
|
|
192
|
-
运行以下命令,自动下载所有文档:
|
|
193
|
-
|
|
194
153
|
```bash
|
|
195
154
|
growork sync
|
|
196
155
|
```
|
|
@@ -207,7 +166,7 @@ docs/
|
|
|
207
166
|
│ ├── prd/
|
|
208
167
|
│ │ └── 用户登录PRD.md
|
|
209
168
|
│ ├── design/
|
|
210
|
-
│ │ └──
|
|
169
|
+
│ │ └── design.md # Figma 链接自动合并到此文件
|
|
211
170
|
│ └── api/
|
|
212
171
|
│ └── 登录接口文档.md
|
|
213
172
|
└── 小优化/
|
|
@@ -222,17 +181,28 @@ growork sync --ver v1.0 # 只同步 v1.0 版本
|
|
|
222
181
|
growork sync -f 用户登录 # 只同步「用户登录」功能
|
|
223
182
|
```
|
|
224
183
|
|
|
225
|
-
|
|
184
|
+
## 命令速查
|
|
226
185
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
186
|
+
| 命令 | 说明 |
|
|
187
|
+
| --------------------------- | -------------- |
|
|
188
|
+
| `growork init` | 创建配置文件 |
|
|
189
|
+
| `growork sync` | 同步所有文档 |
|
|
190
|
+
| `growork sync -c` | 只同步全局文档 |
|
|
191
|
+
| `growork sync --ver v1.0` | 只同步指定版本 |
|
|
192
|
+
| `growork sync -f 功能名` | 只同步指定功能 |
|
|
193
|
+
| `growork list` | 查看文档列表 |
|
|
230
194
|
|
|
231
|
-
|
|
195
|
+
## 支持的文档类型
|
|
196
|
+
|
|
197
|
+
| 平台 | 支持的内容 | 处理方式 |
|
|
198
|
+
| ------ | ---------------------------------------------- | -------------------- |
|
|
199
|
+
| 飞书 | 普通文档 (`/docx/`)、知识库 (`/wiki/`) | 调用 API 下载内容 |
|
|
200
|
+
| Notion | 任意页面(包含数据库的页面会自动转为表格) | 调用 API 下载内容 |
|
|
201
|
+
| Figma | 设计稿链接 (`figma.com/design/...`) | 直接生成链接文档 |
|
|
232
202
|
|
|
233
203
|
## 常见问题
|
|
234
204
|
|
|
235
|
-
###
|
|
205
|
+
### 提示「权限不足」或「401」错误
|
|
236
206
|
|
|
237
207
|
**飞书文档:**
|
|
238
208
|
- 确认应用已开通 `docx:document:readonly` 和 `wiki:wiki:readonly` 权限
|
|
@@ -240,13 +210,9 @@ growork list
|
|
|
240
210
|
- 确认你有权限访问该文档
|
|
241
211
|
|
|
242
212
|
**Notion 页面:**
|
|
243
|
-
- 确认你已经把 Integration
|
|
244
|
-
|
|
245
|
-
---
|
|
213
|
+
- 确认你已经把 Integration 连接到了该页面
|
|
246
214
|
|
|
247
|
-
###
|
|
248
|
-
|
|
249
|
-
Node.js 可能没有正确安装,或者 npm 全局路径没有加入系统 PATH。
|
|
215
|
+
### 提示「找不到命令」或「command not found」
|
|
250
216
|
|
|
251
217
|
尝试重新安装 Node.js,或者使用 `npx` 运行:
|
|
252
218
|
|
|
@@ -254,61 +220,27 @@ Node.js 可能没有正确安装,或者 npm 全局路径没有加入系统 PAT
|
|
|
254
220
|
npx growork sync
|
|
255
221
|
```
|
|
256
222
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
### Q: 同步后文档是空的
|
|
223
|
+
### 同步后文档是空的
|
|
260
224
|
|
|
261
225
|
- 检查文档链接是否正确
|
|
262
226
|
- 检查你是否有权限访问该文档
|
|
263
227
|
- 飞书知识库文档需要使用 `/wiki/` 格式的链接
|
|
264
228
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
### Q: 如何更新已同步的文档?
|
|
229
|
+
### 如何更新已同步的文档?
|
|
268
230
|
|
|
269
231
|
直接再次运行 `growork sync`,会自动覆盖本地文件。
|
|
270
232
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
### Q: 文件名是怎么生成的?
|
|
233
|
+
### 文件名是怎么生成的?
|
|
274
234
|
|
|
275
235
|
文件名会自动从文档标题获取,并处理特殊字符:
|
|
236
|
+
|
|
276
237
|
- `用户登录 / 注册流程 (v1.0)` → `用户登录-注册流程-v1.0.md`
|
|
277
238
|
|
|
278
239
|
你也可以在配置中用 `name` 字段自定义名称。
|
|
279
240
|
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
## 命令速查
|
|
283
|
-
|
|
284
|
-
| 命令 | 说明 |
|
|
285
|
-
| --- | --- |
|
|
286
|
-
| `growork init` | 创建配置文件 |
|
|
287
|
-
| `growork sync` | 同步所有文档 |
|
|
288
|
-
| `growork sync -c` | 只同步全局文档 |
|
|
289
|
-
| `growork sync --ver v1.0` | 只同步指定版本 |
|
|
290
|
-
| `growork sync -f 功能名` | 只同步指定功能 |
|
|
291
|
-
| `growork list` | 查看文档列表 |
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## 支持的文档类型
|
|
296
|
-
|
|
297
|
-
| 平台 | 支持的内容 |
|
|
298
|
-
| --- | --- |
|
|
299
|
-
| 飞书 | 普通文档 (`/docx/`)、知识库 (`/wiki/`) |
|
|
300
|
-
| Notion | 任意页面(包含数据库的页面会自动转为表格) |
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
241
|
## 需要帮助?
|
|
305
242
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
1. 在 GitHub 上提 Issue:https://github.com/anthropics/growork/issues
|
|
309
|
-
2. 检查上面的「常见问题」部分
|
|
310
|
-
|
|
311
|
-
---
|
|
243
|
+
- [GitHub Issues](https://github.com/ThinkerJack/GroWork/issues)
|
|
312
244
|
|
|
313
245
|
## License
|
|
314
246
|
|
package/claude.md
CHANGED
|
@@ -41,11 +41,16 @@ feishu:
|
|
|
41
41
|
notion:
|
|
42
42
|
token: "ntn_xxx"
|
|
43
43
|
|
|
44
|
-
docs
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
outputDir: "docs" # 输出根目录
|
|
45
|
+
|
|
46
|
+
custom: # 全局文档
|
|
47
|
+
- "https://xxx.feishu.cn/docx/xxxxx"
|
|
48
|
+
|
|
49
|
+
versions: # 版本化文档
|
|
50
|
+
v1.0:
|
|
51
|
+
用户登录:
|
|
52
|
+
prd:
|
|
53
|
+
- "https://xxx.feishu.cn/docx/xxxxx"
|
|
49
54
|
```
|
|
50
55
|
|
|
51
56
|
## 常用命令
|
|
@@ -59,11 +64,36 @@ node dist/index.js sync [name] # 同步文档
|
|
|
59
64
|
## 关键类型
|
|
60
65
|
|
|
61
66
|
```typescript
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
// 文档输入格式
|
|
68
|
+
type DocInput = string | { url: string; name?: string };
|
|
69
|
+
|
|
70
|
+
// 功能下的文档分类
|
|
71
|
+
type FeatureValue = DocInput[] | {
|
|
72
|
+
prd?: DocInput[];
|
|
73
|
+
design?: DocInput[];
|
|
74
|
+
api?: DocInput[];
|
|
75
|
+
test?: DocInput[];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// 配置文件结构
|
|
79
|
+
interface GroworkConfigV2 {
|
|
80
|
+
feishu?: FeishuConfig;
|
|
81
|
+
notion?: NotionConfig;
|
|
82
|
+
outputDir?: string;
|
|
83
|
+
custom?: DocInput[];
|
|
84
|
+
versions?: {
|
|
85
|
+
[version: string]: {
|
|
86
|
+
[feature: string]: FeatureValue;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 标准化后的文档
|
|
92
|
+
interface NormalizedDoc {
|
|
65
93
|
url: string;
|
|
66
|
-
|
|
94
|
+
name?: string;
|
|
95
|
+
type: 'feishu' | 'notion';
|
|
96
|
+
outputPath: string; // 含 {title} 占位符
|
|
67
97
|
}
|
|
68
98
|
```
|
|
69
99
|
|
|
@@ -194,7 +224,7 @@ src/
|
|
|
194
224
|
|
|
195
225
|
### 命名
|
|
196
226
|
|
|
197
|
-
-
|
|
227
|
+
- 文件名:小写,如 `feishu.ts`, `config.ts`
|
|
198
228
|
- 函数名:动词开头,如 `syncDoc`, `parseConfig`
|
|
199
229
|
- 布尔变量:is/has/should 开头,如 `isValid`
|
|
200
230
|
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,9 @@ function inferDocType(url) {
|
|
|
26
26
|
if (url.includes("notion.so") || url.includes("notion.site")) {
|
|
27
27
|
return "notion";
|
|
28
28
|
}
|
|
29
|
+
if (url.includes("figma.com")) {
|
|
30
|
+
return "figma";
|
|
31
|
+
}
|
|
29
32
|
throw new Error(`\u65E0\u6CD5\u4ECE URL \u63A8\u65AD\u6587\u6863\u7C7B\u578B: ${url}`);
|
|
30
33
|
}
|
|
31
34
|
function sanitizeFileName(title) {
|
|
@@ -677,10 +680,12 @@ async function syncCommand(options = {}) {
|
|
|
677
680
|
console.log(chalk2.yellow("\u26A0\uFE0F \u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u6863"));
|
|
678
681
|
return;
|
|
679
682
|
}
|
|
683
|
+
const normalDocs = docs.filter((d) => d.type !== "figma");
|
|
684
|
+
const figmaDocs = docs.filter((d) => d.type === "figma");
|
|
680
685
|
const feishuService = config.feishu ? new FeishuService(config.feishu) : null;
|
|
681
686
|
const notionService = config.notion ? new NotionService(config.notion) : null;
|
|
682
|
-
const hasFeishuDocs =
|
|
683
|
-
const hasNotionDocs =
|
|
687
|
+
const hasFeishuDocs = normalDocs.some((d) => d.type === "feishu");
|
|
688
|
+
const hasNotionDocs = normalDocs.some((d) => d.type === "notion");
|
|
684
689
|
if (hasFeishuDocs && !feishuService) {
|
|
685
690
|
throw new Error("\u914D\u7F6E\u6587\u4EF6\u7F3A\u5C11\u98DE\u4E66\u51ED\u8BC1 (feishu.appId, feishu.appSecret)");
|
|
686
691
|
}
|
|
@@ -689,7 +694,16 @@ async function syncCommand(options = {}) {
|
|
|
689
694
|
}
|
|
690
695
|
console.log(chalk2.blue("\u{1F4C4} \u5F00\u59CB\u540C\u6B65\u6587\u6863...\n"));
|
|
691
696
|
let successCount = 0;
|
|
692
|
-
|
|
697
|
+
const figmaGroups = /* @__PURE__ */ new Map();
|
|
698
|
+
for (const doc of figmaDocs) {
|
|
699
|
+
const dir = path3.dirname(doc.outputPath);
|
|
700
|
+
const feature = path3.basename(path3.dirname(dir));
|
|
701
|
+
if (!figmaGroups.has(dir)) {
|
|
702
|
+
figmaGroups.set(dir, { feature, urls: [] });
|
|
703
|
+
}
|
|
704
|
+
figmaGroups.get(dir).urls.push(doc.url);
|
|
705
|
+
}
|
|
706
|
+
for (const doc of normalDocs) {
|
|
693
707
|
const displayName = doc.name || doc.url.slice(-20);
|
|
694
708
|
process.stdout.write(chalk2.gray(` \u23F3 ${displayName}`));
|
|
695
709
|
try {
|
|
@@ -716,11 +730,23 @@ async function syncCommand(options = {}) {
|
|
|
716
730
|
console.log(chalk2.red(` \u2717 ${displayName.padEnd(25)} \u2192 ${errorMessage}`));
|
|
717
731
|
}
|
|
718
732
|
}
|
|
733
|
+
for (const [dir, { feature, urls }] of figmaGroups) {
|
|
734
|
+
const content = `# ${feature} \u8BBE\u8BA1\u7A3F
|
|
735
|
+
|
|
736
|
+
${urls.map((u) => `- ${u}`).join("\n")}
|
|
737
|
+
`;
|
|
738
|
+
const outputPath = path3.join(process.cwd(), dir, "design.md");
|
|
739
|
+
fs3.mkdirSync(path3.dirname(outputPath), { recursive: true });
|
|
740
|
+
fs3.writeFileSync(outputPath, content, "utf-8");
|
|
741
|
+
console.log(chalk2.green(` \u2713 design.md \u2192 ${path3.relative(process.cwd(), outputPath)}`));
|
|
742
|
+
successCount++;
|
|
743
|
+
}
|
|
719
744
|
console.log("");
|
|
720
|
-
|
|
721
|
-
|
|
745
|
+
const totalCount = normalDocs.length + figmaGroups.size;
|
|
746
|
+
if (successCount === totalCount) {
|
|
747
|
+
console.log(chalk2.green(`\u2705 \u540C\u6B65\u5B8C\u6210\uFF0C\u5171 ${totalCount} \u4E2A\u6587\u6863`));
|
|
722
748
|
} else {
|
|
723
|
-
console.log(chalk2.yellow(`\u26A0\uFE0F \u540C\u6B65\u5B8C\u6210: ${successCount}/${
|
|
749
|
+
console.log(chalk2.yellow(`\u26A0\uFE0F \u540C\u6B65\u5B8C\u6210: ${successCount}/${totalCount} \u4E2A\u6587\u6863\u6210\u529F`));
|
|
724
750
|
}
|
|
725
751
|
}
|
|
726
752
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# GroWork 技术架构
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
GroWork 是一个文档同步 CLI 工具,将飞书文档、Notion 页面和 Figma 设计稿同步到本地 Markdown 文件。
|
|
6
|
+
|
|
7
|
+
## 项目结构
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/
|
|
11
|
+
├── index.ts # CLI 入口
|
|
12
|
+
├── commands/
|
|
13
|
+
│ ├── init.ts # growork init
|
|
14
|
+
│ ├── sync.ts # growork sync
|
|
15
|
+
│ └── list.ts # growork list
|
|
16
|
+
├── services/
|
|
17
|
+
│ ├── feishu.ts # 飞书 API 服务
|
|
18
|
+
│ └── notion.ts # Notion API 服务
|
|
19
|
+
└── utils/
|
|
20
|
+
└── config.ts # 配置解析
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 核心模块
|
|
24
|
+
|
|
25
|
+
### 1. CLI 入口 (`src/index.ts`)
|
|
26
|
+
|
|
27
|
+
使用 `commander` 定义命令:
|
|
28
|
+
|
|
29
|
+
- `growork init` - 初始化配置文件
|
|
30
|
+
- `growork sync` - 同步文档,支持 `--ver`、`-f`、`-c` 过滤
|
|
31
|
+
- `growork list` - 列出所有文档
|
|
32
|
+
|
|
33
|
+
### 2. 配置模块 (`src/utils/config.ts`)
|
|
34
|
+
|
|
35
|
+
**核心类型:**
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// 文档输入格式
|
|
39
|
+
type DocInput = string | { url: string; name?: string };
|
|
40
|
+
|
|
41
|
+
// 标准化后的文档
|
|
42
|
+
interface NormalizedDoc {
|
|
43
|
+
url: string;
|
|
44
|
+
name?: string;
|
|
45
|
+
type: 'feishu' | 'notion' | 'figma';
|
|
46
|
+
outputPath: string; // 含 {title} 占位符
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**核心函数:**
|
|
51
|
+
|
|
52
|
+
| 函数 | 作用 |
|
|
53
|
+
|-----|------|
|
|
54
|
+
| `loadConfigV2()` | 加载并解析 YAML 配置文件 |
|
|
55
|
+
| `normalizeConfig()` | 将配置转为标准化的 `NormalizedDoc[]` |
|
|
56
|
+
| `inferDocType()` | 根据 URL 推断文档类型 |
|
|
57
|
+
| `sanitizeFileName()` | 处理文件名特殊字符 |
|
|
58
|
+
|
|
59
|
+
### 3. 同步命令 (`src/commands/sync.ts`)
|
|
60
|
+
|
|
61
|
+
**处理流程:**
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
loadConfigV2() → normalizeConfig() → 分组处理
|
|
65
|
+
├── 飞书文档 → FeishuService.getDocumentAsMarkdown()
|
|
66
|
+
├── Notion 页面 → NotionService.getPageAsMarkdown()
|
|
67
|
+
└── Figma 链接 → 直接生成 design.md
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Figma 处理逻辑:**
|
|
71
|
+
|
|
72
|
+
同一 feature 下的多个 Figma URL 会合并到一个 `design.md` 文件:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// 按目录分组
|
|
76
|
+
const figmaGroups = new Map<string, { feature: string; urls: string[] }>();
|
|
77
|
+
|
|
78
|
+
// 生成内容
|
|
79
|
+
const content = `# ${feature} 设计稿\n\n${urls.map(u => `- ${u}`).join('\n')}\n`;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. 飞书服务 (`src/services/feishu.ts`)
|
|
83
|
+
|
|
84
|
+
**职责:** 调用飞书 API 获取文档内容,转换为 Markdown。
|
|
85
|
+
|
|
86
|
+
**核心方法:**
|
|
87
|
+
|
|
88
|
+
| 方法 | 作用 |
|
|
89
|
+
|-----|------|
|
|
90
|
+
| `getDocumentAsMarkdown(url)` | 主入口,返回完整 Markdown |
|
|
91
|
+
| `parseDocumentId(url)` | 解析 URL 中的文档 ID |
|
|
92
|
+
| `resolveDocumentId(url)` | Wiki 链接需要额外解析真实 ID |
|
|
93
|
+
| `getAllBlocks(docId)` | 分页获取所有 block |
|
|
94
|
+
| `blockToMarkdown(block)` | 将单个 block 转为 Markdown |
|
|
95
|
+
|
|
96
|
+
**支持的 block 类型:**
|
|
97
|
+
|
|
98
|
+
| blockType | 类型 | 输出 |
|
|
99
|
+
|-----------|------|------|
|
|
100
|
+
| 2 | Text | 普通文本 |
|
|
101
|
+
| 3-11 | Heading1-9 | `#` ~ `######` |
|
|
102
|
+
| 12 | Bullet | `- item` |
|
|
103
|
+
| 13 | Ordered | `1. item` |
|
|
104
|
+
| 14 | Code | ` ```lang ``` ` |
|
|
105
|
+
| 15 | Quote | `> text` |
|
|
106
|
+
| 17 | TodoList | `- [ ] task` |
|
|
107
|
+
| 18 | Divider | `---` |
|
|
108
|
+
| 19 | Image | `` |
|
|
109
|
+
| 31 | Table | Markdown 表格 |
|
|
110
|
+
|
|
111
|
+
### 5. Notion 服务 (`src/services/notion.ts`)
|
|
112
|
+
|
|
113
|
+
**职责:** 调用 Notion API 获取页面内容,使用 `notion-to-md` 库转换。
|
|
114
|
+
|
|
115
|
+
**核心方法:**
|
|
116
|
+
|
|
117
|
+
| 方法 | 作用 |
|
|
118
|
+
|-----|------|
|
|
119
|
+
| `getPageAsMarkdown(url)` | 主入口,返回完整 Markdown |
|
|
120
|
+
| `parsePageId(url)` | 从 URL 提取 page ID |
|
|
121
|
+
| `setupCustomTransformers()` | 自定义 child_database 转换 |
|
|
122
|
+
|
|
123
|
+
**数据库处理:**
|
|
124
|
+
|
|
125
|
+
内嵌数据库(child_database)会自动转为 Markdown 表格,支持的字段类型:
|
|
126
|
+
|
|
127
|
+
- title, rich_text, select, multi_select
|
|
128
|
+
- number, checkbox, date, url, files
|
|
129
|
+
|
|
130
|
+
## 数据流
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
┌─────────────────┐
|
|
134
|
+
│ growork.config │ YAML 配置文件
|
|
135
|
+
│ .yaml │
|
|
136
|
+
└────────┬────────┘
|
|
137
|
+
│ loadConfigV2()
|
|
138
|
+
▼
|
|
139
|
+
┌─────────────────┐
|
|
140
|
+
│ GroworkConfigV2 │ 原始配置对象
|
|
141
|
+
└────────┬────────┘
|
|
142
|
+
│ normalizeConfig(options)
|
|
143
|
+
▼
|
|
144
|
+
┌─────────────────┐
|
|
145
|
+
│ NormalizedDoc[] │ 标准化文档列表
|
|
146
|
+
└────────┬────────┘
|
|
147
|
+
│
|
|
148
|
+
┌────┴────┬──────────┐
|
|
149
|
+
▼ ▼ ▼
|
|
150
|
+
Feishu Notion Figma
|
|
151
|
+
│ │ │
|
|
152
|
+
▼ ▼ ▼
|
|
153
|
+
┌─────────────────────────────┐
|
|
154
|
+
│ Markdown 文件写入本地 │
|
|
155
|
+
└─────────────────────────────┘
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 技术栈
|
|
159
|
+
|
|
160
|
+
| 用途 | 依赖 |
|
|
161
|
+
|-----|------|
|
|
162
|
+
| CLI 框架 | commander |
|
|
163
|
+
| 飞书 API | @larksuiteoapi/node-sdk |
|
|
164
|
+
| Notion API | @notionhq/client |
|
|
165
|
+
| Notion 转 MD | notion-to-md |
|
|
166
|
+
| YAML 解析 | yaml |
|
|
167
|
+
| 终端颜色 | chalk |
|
|
168
|
+
| 构建工具 | tsup |
|
|
169
|
+
|
|
170
|
+
## 设计原则
|
|
171
|
+
|
|
172
|
+
1. **最少代码** - 不提前抽象,只实现当前需求
|
|
173
|
+
2. **扁平结构** - 目录层级不超过 2 层
|
|
174
|
+
3. **错误自然抛出** - 在顶层统一处理,不过度防御
|
|
175
|
+
4. **依赖成熟库** - 不重复造轮子
|