Skip to content

Commit 027d659

Browse files
committed
docs: 更新 README.md 和新增 CLAUDE.md 文件,提供使用说明和架构概述
1 parent c0d725c commit 027d659

2 files changed

Lines changed: 121 additions & 68 deletions

File tree

CLAUDE.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
pnpm test # vitest run (single test file: tests/converter.test.ts)
9+
pnpm typecheck # tsc --noEmit
10+
pnpm build # tsup src/index.ts src/cli.ts --format esm --dts --clean --target es2022 --minify
11+
pnpm check # typecheck + test + build (CI gate)
12+
pnpm pack:dry-run # build + dry-run pack for verifying package contents
13+
```
14+
15+
## Architecture
16+
17+
This package converts between Wiki JSON (`item/info` API response) and a custom XML format (`<sklandDocument>`). All paths go through a shared **DocumentModel** intermediate representation.
18+
19+
**Three data formats:**
20+
21+
| Format | Source | Root shape |
22+
|--------|--------|------------|
23+
| Wiki JSON | `item/info` response | `InfoRoot { data: { item: InfoItem } }` or bare `InfoItem` |
24+
| XML | `<sklandDocument>` with `<itemId>`, `<metainfo>`, `<description>`, `<chapters>` | Custom schema |
25+
| DocumentModel | Internal only | `src/model.ts` — typed block/inline tree |
26+
27+
**Core pipeline:**
28+
29+
```
30+
Wiki JSON ──[jsonFormat.ts:documentFromJsonText]──> DocumentModel ──[xmlFormat.ts:documentToXmlText]──> XML
31+
XML ────────[xmlFormat.ts:documentFromXmlText]───> DocumentModel ──[jsonFormat.ts:documentToJsonText]──> Wiki JSON
32+
```
33+
34+
**Key modules:**
35+
36+
- `src/model.ts` — DocumentModel types (Block, Inline, Chapter, etc.) plus guards (`isParagraph`, `isTextRun`), constructors (`textRun`, `paragraph`), and `normalizeBlocks` which trims empty paragraphs and merges adjacent text runs.
37+
- `src/jsonFormat.ts` — Parses nested JSON payload (blockMap/childIds/itemMap structure) into DocumentModel; renders DocumentModel back to the same JSON structure with fresh IDs via IdFactory.
38+
- `src/xmlFormat.ts` — Parses XML elements (h1-h3, ul/ol, table, img, inline tags) into DocumentModel; renders DocumentModel to XML string via manual string building (no DOM construction).
39+
- `src/convert.ts` — Public conversion API (`wikiJsonToXml`, `xmlToWikiJson`, batch variants, `convert`). Batch functions are fail-fast: any entry failure throws immediately.
40+
- `src/cli.ts` — CLI entry (`xml convert --from json|xml --to json|xml`). Uses a `CONVERSION_MAP` lookup table keyed by `"from->to"`.
41+
- `src/ids.ts``IdFactory` generates random alphanumeric IDs (widget, block, item, tab) using `crypto.getRandomValues`.
42+
- `src/xmlDom.ts` — XML parsing wrapper. Prefers native `globalThis.DOMParser` when available, falls back to `@xmldom/xmldom`.
43+
- `src/colors.ts` / `src/constants.ts` — Color name mappings (`JSON_TO_XML_COLOR`, `XML_TO_JSON_COLOR`), inline/block tag sets, entry type mappings, table width defaults.
44+
45+
**JSON input shape detection** (`extractInfoItem` in `jsonFormat.ts`):
46+
- Has `data.item` → InfoRoot (strips envelope, saves `infoRootMeta`)
47+
- Has `brief` and `document` → InfoItem directly
48+
49+
**XML parsing** uses two-tier detection: native `DOMParser.parseFromString` first, `@xmldom/xmldom` fallback. Both paths check for `<parsererror>` element after parsing.
50+
51+
**Round-trip semantics:** Conversion targets DocumentModel equivalence, not byte-level identity. Rendered JSON regenerates all internal IDs (`widgetCommonMap`, `documentMap`, block ids) while preserving referential integrity.
52+
53+
**The `warnings` field** in `ConversionResult` exists in the type signature but is never populated in current code paths — all converters return `[]`.

README.md

Lines changed: 68 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
# @eihrteam/xml
22

3-
`@eihrteam/xml` 是 SKLand 终末地 Wiki 正式页 JSON 与 XML 的本地互转工具包。
4-
5-
本包处理 `item/info` 的公开读取模型:输入 JSON 可以是完整响应包裹
6-
`InfoRoot`,也可以是 `data.item` 里的 `InfoItem`。它不发送网络请求,不处理
7-
`item/update` 提交体,不签名请求,不管理 `Did``Cred`、token,也不做草稿清理、
8-
diff 或 API replay。
3+
SKLand Endfield Wiki JSON 与 XML 的本地互转工具包。处理 `item/info` 公开读取模型,不含网络请求、签名、认证或 API replay。
94

105
## 安装
116

@@ -18,27 +13,68 @@ pnpm add @eihrteam/xml
1813
```ts
1914
import { wikiJsonToXml, wikiJsonToXmlBatch, xmlToWikiJson } from '@eihrteam/xml'
2015

16+
// InfoRoot 或 InfoItem JSON -> XML
2117
const xml = wikiJsonToXml(infoRootJsonText).text
18+
19+
// 批量转换
2220
const batch = wikiJsonToXmlBatch([
2321
{ source: infoRootJsonText, meta: { itemId: '1', path: '终末地百科/物品/id1.json' } },
2422
])
23+
24+
// XML -> InfoItem JSON
2525
const infoItemJson = xmlToWikiJson(xml).text
26+
27+
// XML -> InfoRoot JSON(带外层 envelope)
2628
const infoRootJson = xmlToWikiJson(xml, { wrapInfoRoot: true }).text
2729
```
2830

31+
## CLI
32+
33+
```bash
34+
# JSON -> XML
35+
xml convert --from json --to xml --input item.json --output item.xml
36+
37+
# XML -> JSON
38+
xml convert --from xml --to json --input item.xml --output item.json
39+
40+
# 管道模式
41+
cat item.json | xml convert --from json --to xml
42+
```
43+
44+
格式说明:`json` 对应 wiki InfoItem/InfoRoot JSON,`xml` 对应 `<sklandDocument>` XML。
45+
2946
## API
3047

31-
- `xmlToWikiJson(xml, options?)`
32-
- `wikiJsonToXml(json)`
33-
- `wikiJsonToXmlBatch(entries)`
34-
- `convert(source, { from, to, ...options })`
35-
- `parseWikiJson(source)`
36-
- `parseXml(source)`
37-
- `renderWikiJson(document, options?)`
38-
- `renderXml(document)`
39-
- `XmlWikiConversionError`
48+
### 转换函数
49+
50+
| 函数 | 说明 |
51+
|------|------|
52+
| `wikiJsonToXml(json)` | InfoItem/InfoRoot JSON -> XML |
53+
| `xmlToWikiJson(xml, options?)` | XML -> InfoItem/InfoRoot JSON |
54+
| `wikiJsonToXmlBatch(entries)` | 批量 JSON -> XML |
55+
| `xmlToWikiJsonBatch(entries, options?)` | 批量 XML -> JSON |
56+
| `convert(source, options)` | 通用双向转换 |
57+
58+
### 解析与渲染
59+
60+
| 函数 | 说明 |
61+
|------|------|
62+
| `parseWikiJson(source)` | 解析 JSON 为 `DocumentModel` |
63+
| `parseXml(source)` | 解析 XML 为 `DocumentModel` |
64+
| `renderXml(document)` | `DocumentModel` 渲染为 XML |
65+
| `renderWikiJson(document, options?)` | `DocumentModel` 渲染为 JSON |
4066

41-
只支持 `'xml'``'wiki-json'` 两种格式。`ConversionResult` 返回:
67+
### 类型
68+
69+
| 导出 | 说明 |
70+
|------|------|
71+
| `XmlWikiConversionError` | 转换过程中的错误类型 |
72+
| `ConvertOptions` | `convert()` 的选项类型 |
73+
| `RenderWikiJsonOptions` | `renderWikiJson()` 的选项类型 |
74+
| `ConversionResult` | 返回 `{ text, warnings }` |
75+
| `DocumentModel` 及相关 block/inline 类型 | 中间表示模型 |
76+
77+
### ConversionResult
4278

4379
```ts
4480
interface ConversionResult {
@@ -47,8 +83,9 @@ interface ConversionResult {
4783
}
4884
```
4985

50-
`wikiJsonToXmlBatch(entries)` 接收 `{ source, meta? }[]`,其中 `source` 是单个
51-
`InfoRoot``InfoItem`。返回值为:
86+
### 批量转换
87+
88+
`wikiJsonToXmlBatch(entries)` 接收 `{ source, meta? }[]``source` 为单个 InfoRoot 或 InfoItem(字符串或对象)。返回:
5289

5390
```ts
5491
interface WikiJsonToXmlBatchResult<TMeta = unknown> {
@@ -57,62 +94,25 @@ interface WikiJsonToXmlBatchResult<TMeta = unknown> {
5794
}
5895
```
5996

60-
每个 `items[]` 元素保留输入 `meta` 和单项 `warnings`;顶层 `warnings` 是带 batch
61-
index 前缀的汇总。任一条目转换失败会直接抛错,错误信息包含失败的 batch index。
62-
63-
## 数据范围
64-
65-
- JSON 范围是 `item/info` 正式页结构,不是通用 JSON,也不是 `item/update` 提交结构。
66-
- XML 根节点是 `<sklandDocument>`
67-
- `<publicMeta>` 保存正式页元数据,例如 `lang``status``tagIds``createdUser`
68-
`lastUpdatedUser``publishedAtTs``lastAuditPassedAt``mainType``subType` 等。
69-
- `brief.description: null` 会按正式页的空简介状态保留,XML 中表现为
70-
`<description source="null">`
71-
72-
## 兼容性说明
97+
每个 item 保留输入 `meta` 和单项 `warnings`;顶层 `warnings` 带 batch index 前缀的汇总。单条失败直接抛错,错误信息含 batch index。
7398

74-
转换以 `DocumentModel` 语义等价为目标,不承诺字节级 round-trip。生成 JSON 时会重新生成
75-
`widgetCommonMap``documentMap`、block、tab、audio 等内部 id,但会保持引用关系一致。
99+
`xmlToWikiJsonBatch(entries, options?)` 同理,接收 XML 字符串数组。
76100

77-
为兼容 `TheSklandDataSource` 当前正式页数据,本包接受以下公开响应边界值:
101+
## 数据范围
78102

79-
- 空章节标题。
80-
-`content` / `intro.description` 文档引用。
81-
- `brief.description: null`
82-
- 图片 URL 无法反推图片 id/format 时,由 XML 显式保存 `<id>``<format>`
83-
- 正式页中低于 100px 的表格列宽。
84-
- 正式页中已存在的复杂表格单元格覆盖关系。
103+
- JSON 范围是 `item/info` 正式页结构(InfoRoot 或 InfoItem),非通用 JSON。
104+
- XML 根节点为 `<sklandDocument>`
105+
- `<publicMeta>` 保存正式页元数据,如 `lang``status``tagIds``createdUser``lastUpdatedUser``publishedAtTs``mainType``subType` 等。
106+
- `brief.description: null` 会保留空简介状态,XML 中表现为 `<description source="null">`
85107

86-
`~/TheSklandDataSource` 若改用 XML 作为主数据储存格式,建议只在输入层增加 loader:
108+
### 兼容性说明
87109

88-
1. `index.json.files` 仍作为遍历入口。
89-
2. 每个 XML 文件读取后调用 `xmlToWikiJson(xml, { wrapInfoRoot: true })`
90-
3. 将转换得到的 `InfoRoot` 交给现有构建、提取和配方转换逻辑。
91-
4. 输出、下载资源、配方抽取、`entry` 关系解析继续由 `TheSklandDataSource` 负责。
110+
转换以 `DocumentModel` 语义等价为目标,不承诺字节级 round-trip。生成 JSON 时会重新生成 `widgetCommonMap``documentMap`、block、tab、audio 等内部 id,但保持引用关系一致。
92111

93-
`item/info``item/update` 的网络模型差异不属于本包职责
112+
接受的公开响应边界值:空章节标题、空 `content` / `intro.description` 文档引用、`brief.description: null`、图片 URL 无法反推 id/format 时由 XML 显式保存 `<id>``<format>`、低于 100px 的表格列宽、复杂表格单元格覆盖关系
94113

95114
## 运行时
96115

97-
- Node.js 是主要运行环境,要求 Node 20 或更高版本。
98-
- 浏览器环境优先使用原生 `globalThis.DOMParser`
99-
- Node 环境使用 `@xmldom/xmldom`,不依赖 `jsdom`
100-
101-
## 发布流程
102-
103-
仓库包含两个 GitHub Actions workflow:
104-
105-
- `CI`:在 `main`、Pull Request 和手动触发时运行 Node 20/22/24 的
106-
`pnpm check`,并在 Node 24 上校验 npm 包内容。
107-
- `Publish`:在 GitHub Release 发布或手动指定 tag 时运行检查、校验
108-
`package.json` 版本与 tag 一致,然后执行 `npm publish --access public --provenance`
109-
110-
推荐发布步骤:
111-
112-
1. 更新 `package.json``version`
113-
2. 提交并推送。
114-
3. 创建匹配版本的 tag,例如 `v0.1.0`
115-
4. 在 GitHub 创建并发布 Release,触发 npm 发布。
116-
117-
发布到 npm 推荐使用 npm Trusted Publishing;如果未启用 Trusted Publishing,也可以在
118-
GitHub 仓库的 Actions secrets 中配置 `NPM_TOKEN` 作为回退。
116+
- Node.js 20 及以上。
117+
- 浏览器环境优先使用 `globalThis.DOMParser`
118+
- Node 环境使用 `@xmldom/xmldom`

0 commit comments

Comments
 (0)