@hupan56/wlkj 2.3.0 → 2.3.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/package.json
CHANGED
|
@@ -24,31 +24,64 @@ from typing import Dict, List, Optional
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def find_repowiki_dirs(code_dir: str) -> List[tuple]:
|
|
27
|
-
"""扫描 data/code/{project}/.qoder/repowiki/, 返回 [(project, wiki_root), ...]。
|
|
27
|
+
"""扫描 data/code/{project}/.qoder/repowiki/, 返回 [(project, wiki_root), ...]。
|
|
28
|
+
|
|
29
|
+
健壮性: repowiki 目录可能存在但为空/损坏/正在生成中。
|
|
30
|
+
只返回 metadata 文件存在且非空的目录。
|
|
31
|
+
"""
|
|
28
32
|
results = []
|
|
29
33
|
if not os.path.isdir(code_dir):
|
|
30
34
|
return results
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
try:
|
|
36
|
+
projects = os.listdir(code_dir)
|
|
37
|
+
except OSError:
|
|
38
|
+
return results
|
|
39
|
+
for proj in projects:
|
|
40
|
+
try:
|
|
41
|
+
wiki_base = os.path.join(code_dir, proj, '.qoder', 'repowiki')
|
|
42
|
+
if not os.path.isdir(wiki_base):
|
|
43
|
+
continue
|
|
44
|
+
for lang in os.listdir(wiki_base):
|
|
45
|
+
lang_dir = os.path.join(wiki_base, lang)
|
|
46
|
+
if not os.path.isdir(lang_dir):
|
|
47
|
+
continue
|
|
48
|
+
meta = os.path.join(lang_dir, 'meta', 'repowiki-metadata.json')
|
|
49
|
+
if not os.path.isfile(meta):
|
|
50
|
+
continue
|
|
51
|
+
# 校验文件非空 (生成中可能写出 0 字节文件)
|
|
52
|
+
try:
|
|
53
|
+
if os.path.getsize(meta) < 10:
|
|
54
|
+
continue # 太小, 可能正在生成
|
|
55
|
+
except OSError:
|
|
56
|
+
continue
|
|
40
57
|
results.append((proj, lang_dir))
|
|
41
58
|
break # 每个项目只取一个语言版本
|
|
59
|
+
except OSError:
|
|
60
|
+
continue # 权限/路径问题, 跳过这个项目
|
|
42
61
|
return results
|
|
43
62
|
|
|
44
63
|
|
|
45
64
|
def load_wiki_metadata(wiki_root: str) -> Optional[dict]:
|
|
46
|
-
"""加载 repowiki-metadata.json。
|
|
65
|
+
"""加载 repowiki-metadata.json。
|
|
66
|
+
|
|
67
|
+
健壮性: 文件可能不存在/空/损坏/正在生成。
|
|
68
|
+
"""
|
|
47
69
|
meta_path = os.path.join(wiki_root, 'meta', 'repowiki-metadata.json')
|
|
48
70
|
try:
|
|
71
|
+
if not os.path.isfile(meta_path):
|
|
72
|
+
return None
|
|
73
|
+
if os.path.getsize(meta_path) < 10:
|
|
74
|
+
return None # 正在生成或空
|
|
49
75
|
with open(meta_path, encoding='utf-8') as f:
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
data = json.load(f)
|
|
77
|
+
# 校验基本结构
|
|
78
|
+
if not isinstance(data, dict):
|
|
79
|
+
return None
|
|
80
|
+
# wiki_catalogs 可能为空 (生成中)
|
|
81
|
+
if not data.get('wiki_catalogs'):
|
|
82
|
+
return None # 没有内容, 视为无效
|
|
83
|
+
return data
|
|
84
|
+
except (OSError, json.JSONDecodeError, UnicodeDecodeError):
|
|
52
85
|
return None
|
|
53
86
|
|
|
54
87
|
|
|
@@ -73,76 +106,100 @@ def build_wiki_index(code_dir: str, output_path: str) -> dict:
|
|
|
73
106
|
"""扫描所有项目的 Repo Wiki, 构建 wiki-index.json。
|
|
74
107
|
|
|
75
108
|
Returns:
|
|
76
|
-
|
|
109
|
+
{'index': {...}, 'stats': {...}} 或 {'index': {}, 'stats': {...}}
|
|
110
|
+
始终返回含 'index' 和 'stats' 两个 key 的 dict (调用方可安全 .get)。
|
|
111
|
+
wiki 不存在/为空/损坏时 index 为 {}, stats 计数为 0。
|
|
77
112
|
"""
|
|
78
|
-
wiki_dirs = find_repowiki_dirs(code_dir)
|
|
79
|
-
if not wiki_dirs:
|
|
80
|
-
return {}
|
|
81
|
-
|
|
82
113
|
index = {} # keyword -> [entry, ...]
|
|
83
114
|
stats = {'projects': 0, 'pages': 0, 'keywords': 0}
|
|
84
115
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
catalogs = meta.get('wiki_catalogs', [])
|
|
92
|
-
items = meta.get('wiki_items', [])
|
|
93
|
-
# catalog_id -> title 映射
|
|
94
|
-
id_to_title = {it.get('catalog_id'): it.get('title', '') for it in items}
|
|
95
|
-
|
|
96
|
-
for cat in catalogs:
|
|
97
|
-
cat_id = cat.get('id', '')
|
|
98
|
-
name = cat.get('name', '')
|
|
99
|
-
desc = cat.get('description', '')
|
|
100
|
-
dep_files = cat.get('dependent_files', '')
|
|
101
|
-
title = id_to_title.get(cat_id, name)
|
|
102
|
-
|
|
103
|
-
# 找对应的 markdown 文档
|
|
104
|
-
md_path = _find_md_for_catalog(wiki_root, cat_id, name)
|
|
105
|
-
if not md_path:
|
|
106
|
-
continue
|
|
116
|
+
wiki_dirs = find_repowiki_dirs(code_dir)
|
|
117
|
+
if not wiki_dirs:
|
|
118
|
+
# wiki 不存在或都损坏 → 写空索引, 避免每次搜索都重建
|
|
119
|
+
_write_wiki_index(output_path, index, stats)
|
|
120
|
+
return {'index': index, 'stats': stats}
|
|
107
121
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
for project, wiki_root in wiki_dirs:
|
|
123
|
+
try:
|
|
124
|
+
meta = load_wiki_metadata(wiki_root)
|
|
125
|
+
if not meta:
|
|
126
|
+
continue # 这个项目的 wiki 损坏/为空, 跳过
|
|
127
|
+
stats['projects'] += 1
|
|
128
|
+
|
|
129
|
+
catalogs = meta.get('wiki_catalogs') or []
|
|
130
|
+
items = meta.get('wiki_items') or []
|
|
131
|
+
# catalog_id -> title 映射
|
|
132
|
+
id_to_title = {}
|
|
133
|
+
for it in items:
|
|
134
|
+
if isinstance(it, dict):
|
|
135
|
+
cid = it.get('catalog_id')
|
|
136
|
+
if cid:
|
|
137
|
+
id_to_title[cid] = it.get('title', '')
|
|
138
|
+
|
|
139
|
+
for cat in catalogs:
|
|
140
|
+
try:
|
|
141
|
+
if not isinstance(cat, dict):
|
|
142
|
+
continue
|
|
143
|
+
cat_id = cat.get('id', '')
|
|
144
|
+
name = cat.get('name', '') or ''
|
|
145
|
+
desc = cat.get('description', '') or ''
|
|
146
|
+
dep_files = cat.get('dependent_files', '') or ''
|
|
147
|
+
title = id_to_title.get(cat_id, name)
|
|
148
|
+
|
|
149
|
+
# 找对应的 markdown 文档 (可能不存在, 生成中)
|
|
150
|
+
md_path = _find_md_for_catalog(wiki_root, cat_id, name)
|
|
151
|
+
if not md_path:
|
|
152
|
+
continue # 文档还没生成, 跳过
|
|
153
|
+
|
|
154
|
+
stats['pages'] += 1
|
|
155
|
+
|
|
156
|
+
# 提取关键词
|
|
157
|
+
kw_text = ' '.join([str(title), str(name), str(desc)])
|
|
158
|
+
if dep_files:
|
|
159
|
+
kw_text += ' ' + str(dep_files).replace(',', ' ').replace('/', ' ')
|
|
160
|
+
keywords = _extract_keywords(kw_text)
|
|
161
|
+
|
|
162
|
+
entry = {
|
|
163
|
+
'project': project,
|
|
164
|
+
'title': str(title or name),
|
|
165
|
+
'catalog_id': str(cat_id),
|
|
166
|
+
'md_path': md_path,
|
|
167
|
+
'dependent_files': [f.strip() for f in str(dep_files).split(',') if f.strip()],
|
|
168
|
+
'description': str(desc),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
for kw in keywords:
|
|
172
|
+
index.setdefault(kw, [])
|
|
173
|
+
if entry not in index[kw]:
|
|
174
|
+
index[kw].append(entry)
|
|
175
|
+
except (OSError, TypeError, ValueError):
|
|
176
|
+
continue # 单条 catalog 出错不影响其他
|
|
177
|
+
except (OSError, TypeError):
|
|
178
|
+
continue # 整个项目的 wiki 出错, 跳过
|
|
130
179
|
|
|
131
180
|
stats['keywords'] = len(index)
|
|
181
|
+
_write_wiki_index(output_path, index, stats)
|
|
182
|
+
return {'index': index, 'stats': stats}
|
|
132
183
|
|
|
133
|
-
# 写入
|
|
134
|
-
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
135
|
-
with open(output_path, 'w', encoding='utf-8') as f:
|
|
136
|
-
json.dump({'index': index, 'stats': stats}, f, ensure_ascii=False, indent=2)
|
|
137
184
|
|
|
138
|
-
|
|
185
|
+
def _write_wiki_index(output_path: str, index: dict, stats: dict) -> None:
|
|
186
|
+
"""安全写入 wiki-index.json (best-effort, 失败不崩)。"""
|
|
187
|
+
try:
|
|
188
|
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
189
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
190
|
+
json.dump({'index': index, 'stats': stats}, f, ensure_ascii=False, indent=2)
|
|
191
|
+
except OSError:
|
|
192
|
+
pass
|
|
139
193
|
|
|
140
194
|
|
|
141
195
|
def _find_md_for_catalog(wiki_root: str, catalog_id: str, name: str) -> Optional[str]:
|
|
142
196
|
"""在 content/ 目录下找对应的 markdown 文件。
|
|
143
197
|
|
|
144
198
|
文件名通常等于 catalog name (.md), 在某个子分类目录下。
|
|
199
|
+
健壮性: name 为空/content 目录不存在时返回 None。
|
|
145
200
|
"""
|
|
201
|
+
if not name:
|
|
202
|
+
return None
|
|
146
203
|
content_dir = os.path.join(wiki_root, 'content')
|
|
147
204
|
if not os.path.isdir(content_dir):
|
|
148
205
|
return None
|