@zengjing/xmind-mcp 1.0.0
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/LICENSE +21 -0
- package/README.md +574 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +170 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/legacy-parser.d.ts +63 -0
- package/dist/core/legacy-parser.d.ts.map +1 -0
- package/dist/core/legacy-parser.js +139 -0
- package/dist/core/legacy-parser.js.map +1 -0
- package/dist/core/parser.d.ts +19 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +111 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/zen-parser.d.ts +15 -0
- package/dist/core/zen-parser.d.ts.map +1 -0
- package/dist/core/zen-parser.js +90 -0
- package/dist/core/zen-parser.js.map +1 -0
- package/dist/formatters/json-formatter.d.ts +69 -0
- package/dist/formatters/json-formatter.d.ts.map +1 -0
- package/dist/formatters/json-formatter.js +131 -0
- package/dist/formatters/json-formatter.js.map +1 -0
- package/dist/formatters/markdown-formatter.d.ts +40 -0
- package/dist/formatters/markdown-formatter.d.ts.map +1 -0
- package/dist/formatters/markdown-formatter.js +136 -0
- package/dist/formatters/markdown-formatter.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +117 -0
- package/dist/index.js.map +1 -0
- package/dist/model/schemas.d.ts +54 -0
- package/dist/model/schemas.d.ts.map +1 -0
- package/dist/model/schemas.js +64 -0
- package/dist/model/schemas.js.map +1 -0
- package/dist/model/types.d.ts +79 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +6 -0
- package/dist/model/types.js.map +1 -0
- package/dist/tools/branch-tool.d.ts +20 -0
- package/dist/tools/branch-tool.d.ts.map +1 -0
- package/dist/tools/branch-tool.js +150 -0
- package/dist/tools/branch-tool.js.map +1 -0
- package/dist/tools/parse-tool.d.ts +27 -0
- package/dist/tools/parse-tool.d.ts.map +1 -0
- package/dist/tools/parse-tool.js +126 -0
- package/dist/tools/parse-tool.js.map +1 -0
- package/dist/tools/search-tool.d.ts +27 -0
- package/dist/tools/search-tool.d.ts.map +1 -0
- package/dist/tools/search-tool.js +179 -0
- package/dist/tools/search-tool.js.map +1 -0
- package/dist/utils/errors.d.ts +26 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +41 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file-utils.d.ts +25 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +58 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +23 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +67 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI tool for extracting and analyzing XMind files.
|
|
4
|
+
* Usage: node dist/cli.js <file-path> [options]
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { parseXMindFile } from './core/parser.js';
|
|
9
|
+
import { xmindToMarkdown } from './formatters/markdown-formatter.js';
|
|
10
|
+
import { xmindToJSON } from './formatters/json-formatter.js';
|
|
11
|
+
import { isXMindError } from './utils/errors.js';
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
15
|
+
printHelp();
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
const filePath = args[0];
|
|
19
|
+
const options = parseArgs(args.slice(1));
|
|
20
|
+
try {
|
|
21
|
+
// Resolve file path (support ~ expansion)
|
|
22
|
+
const resolvedPath = filePath.startsWith('~')
|
|
23
|
+
? path.join(process.env.HOME || '/', filePath.slice(1))
|
|
24
|
+
: path.resolve(filePath);
|
|
25
|
+
// Check file exists
|
|
26
|
+
try {
|
|
27
|
+
await fs.access(resolvedPath);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
console.error(`ā File not found: ${resolvedPath}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
if (options.verbose) {
|
|
34
|
+
console.log(`š Parsing: ${resolvedPath}`);
|
|
35
|
+
}
|
|
36
|
+
// Parse the file
|
|
37
|
+
const result = await parseXMindFile(resolvedPath);
|
|
38
|
+
// Display metadata
|
|
39
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
40
|
+
console.log('š XMind File Information');
|
|
41
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
42
|
+
console.log(`Format: ${result.meta?.formatVersion}`);
|
|
43
|
+
console.log(`Sheets: ${result.sheets.length}`);
|
|
44
|
+
console.log(`Total Topics: ${result.meta?.topicCount}`);
|
|
45
|
+
console.log(`Parsed at: ${result.meta?.parsedAt}`);
|
|
46
|
+
if (options.verbose) {
|
|
47
|
+
result.sheets.forEach((sheet, idx) => {
|
|
48
|
+
console.log(` Sheet ${idx + 1}: "${sheet.title}"`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// Format content
|
|
52
|
+
let content;
|
|
53
|
+
if (options.format === 'json') {
|
|
54
|
+
content = xmindToJSON(result);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
content = xmindToMarkdown(result);
|
|
58
|
+
}
|
|
59
|
+
// Handle search
|
|
60
|
+
if (options.search) {
|
|
61
|
+
content = filterContentBySearch(content, options.search, options.format);
|
|
62
|
+
}
|
|
63
|
+
// Output
|
|
64
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
65
|
+
console.log(`š Content (${options.format.toUpperCase()} format)`);
|
|
66
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
67
|
+
console.log(content);
|
|
68
|
+
// Save to file if specified
|
|
69
|
+
if (options.output) {
|
|
70
|
+
await fs.writeFile(options.output, content, 'utf-8');
|
|
71
|
+
console.log(`\nā
Content saved to: ${options.output}`);
|
|
72
|
+
}
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (isXMindError(error)) {
|
|
77
|
+
console.error(`\nā Error: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
else if (error instanceof Error) {
|
|
80
|
+
console.error(`\nā Unexpected error: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.error('\nā An unknown error occurred');
|
|
84
|
+
}
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function parseArgs(args) {
|
|
89
|
+
const options = {
|
|
90
|
+
format: 'markdown',
|
|
91
|
+
verbose: false,
|
|
92
|
+
};
|
|
93
|
+
for (let i = 0; i < args.length; i++) {
|
|
94
|
+
switch (args[i]) {
|
|
95
|
+
case '--format':
|
|
96
|
+
case '-f':
|
|
97
|
+
if (args[i + 1] && (args[i + 1] === 'json' || args[i + 1] === 'markdown')) {
|
|
98
|
+
options.format = args[i + 1];
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case '--output':
|
|
103
|
+
case '-o':
|
|
104
|
+
if (args[i + 1]) {
|
|
105
|
+
options.output = args[i + 1];
|
|
106
|
+
i++;
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
case '--search':
|
|
110
|
+
case '-s':
|
|
111
|
+
if (args[i + 1]) {
|
|
112
|
+
options.search = args[i + 1];
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case '--verbose':
|
|
117
|
+
case '-v':
|
|
118
|
+
options.verbose = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return options;
|
|
123
|
+
}
|
|
124
|
+
function filterContentBySearch(content, query, format) {
|
|
125
|
+
const lines = content.split('\n');
|
|
126
|
+
const filtered = lines.filter(line => line.toLowerCase().includes(query.toLowerCase()));
|
|
127
|
+
if (filtered.length === 0) {
|
|
128
|
+
return `No results found for: "${query}"`;
|
|
129
|
+
}
|
|
130
|
+
return filtered.join('\n');
|
|
131
|
+
}
|
|
132
|
+
function printHelp() {
|
|
133
|
+
console.log(`
|
|
134
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
135
|
+
ā XMind Parser CLI - Extract Mind Map Content ā
|
|
136
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
137
|
+
|
|
138
|
+
USAGE:
|
|
139
|
+
node dist/cli.js <file-path> [options]
|
|
140
|
+
|
|
141
|
+
ARGUMENTS:
|
|
142
|
+
<file-path> Path to the .xmind file (supports ~ for home directory)
|
|
143
|
+
|
|
144
|
+
OPTIONS:
|
|
145
|
+
-f, --format Output format: 'markdown' (default) or 'json'
|
|
146
|
+
-o, --output Save output to file (optional)
|
|
147
|
+
-s, --search Filter results by keyword (optional)
|
|
148
|
+
-v, --verbose Show detailed information
|
|
149
|
+
-h, --help Show this help message
|
|
150
|
+
|
|
151
|
+
EXAMPLES:
|
|
152
|
+
# Basic usage - print to console
|
|
153
|
+
node dist/cli.js ~/Documents/my-mindmap.xmind
|
|
154
|
+
|
|
155
|
+
# JSON format
|
|
156
|
+
node dist/cli.js ~/Documents/my-mindmap.xmind --format json
|
|
157
|
+
|
|
158
|
+
# Save to file
|
|
159
|
+
node dist/cli.js ~/Documents/my-mindmap.xmind -o output.md
|
|
160
|
+
|
|
161
|
+
# Search and filter
|
|
162
|
+
node dist/cli.js ~/Documents/my-mindmap.xmind --search "important"
|
|
163
|
+
|
|
164
|
+
# Verbose with output file
|
|
165
|
+
node dist/cli.js ~/Documents/my-mindmap.xmind -v -o result.json --format json
|
|
166
|
+
|
|
167
|
+
`);
|
|
168
|
+
}
|
|
169
|
+
main();
|
|
170
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for legacy XMind format (XML-based, versions 8 and earlier).
|
|
3
|
+
* Legacy XMind files use XML structure within the ZIP container.
|
|
4
|
+
*/
|
|
5
|
+
import type { XMindNode, XMindSheet } from '../model/types.js';
|
|
6
|
+
interface LegacyTopic {
|
|
7
|
+
'@_id': string;
|
|
8
|
+
'@_href'?: string;
|
|
9
|
+
title?: string | {
|
|
10
|
+
'#text': string;
|
|
11
|
+
};
|
|
12
|
+
'notes'?: string | {
|
|
13
|
+
'#text': string;
|
|
14
|
+
};
|
|
15
|
+
markers?: {
|
|
16
|
+
marker?: Array<{
|
|
17
|
+
'@_markerId': string;
|
|
18
|
+
}> | {
|
|
19
|
+
'@_markerId': string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
labels?: {
|
|
23
|
+
label?: Array<{
|
|
24
|
+
'#text': string;
|
|
25
|
+
}> | {
|
|
26
|
+
'#text': string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
topic?: LegacyTopic[] | LegacyTopic;
|
|
30
|
+
children?: {
|
|
31
|
+
topics?: Array<{
|
|
32
|
+
topic?: LegacyTopic[] | LegacyTopic;
|
|
33
|
+
'@_type'?: string;
|
|
34
|
+
}> | {
|
|
35
|
+
topic?: LegacyTopic[] | LegacyTopic;
|
|
36
|
+
'@_type'?: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
'relationship'?: Array<{
|
|
40
|
+
'@_id': string;
|
|
41
|
+
'@_end': string;
|
|
42
|
+
'@_sourceId': string;
|
|
43
|
+
'@_targetId': string;
|
|
44
|
+
'@_title'?: string;
|
|
45
|
+
}> | {
|
|
46
|
+
'@_id': string;
|
|
47
|
+
'@_end': string;
|
|
48
|
+
'@_sourceId': string;
|
|
49
|
+
'@_targetId': string;
|
|
50
|
+
'@_title'?: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Transform raw Legacy XML topic to normalized XMindNode.
|
|
55
|
+
* Recursively processes child topics.
|
|
56
|
+
*/
|
|
57
|
+
export declare function transformLegacyTopic(legacyTopic: LegacyTopic): XMindNode;
|
|
58
|
+
/**
|
|
59
|
+
* Parse Legacy XML format content into XMindSheet objects.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseLegacyContent(xmlText: string): Promise<XMindSheet[]>;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=legacy-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-parser.d.ts","sourceRoot":"","sources":["../../src/core/legacy-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/D,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG;YAAE,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1D,CAAC;IACF,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;IAEpC,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG;YAAE,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KACzI,CAAC;IACF,cAAc,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnO;AAqCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS,CAoDxE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA+D/E"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for legacy XMind format (XML-based, versions 8 and earlier).
|
|
3
|
+
* Legacy XMind files use XML structure within the ZIP container.
|
|
4
|
+
*/
|
|
5
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
6
|
+
import { ParseError } from '../utils/errors.js';
|
|
7
|
+
import { debug } from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a single item or array to always be an array.
|
|
10
|
+
* fast-xml-parser returns a single object if there's one item,
|
|
11
|
+
* and an array if there are multiple items.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeArray(value) {
|
|
14
|
+
if (!value)
|
|
15
|
+
return [];
|
|
16
|
+
return Array.isArray(value) ? value : [value];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Extract text from a string or object with #text property.
|
|
20
|
+
*/
|
|
21
|
+
function extractText(value) {
|
|
22
|
+
if (!value)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (typeof value === 'string')
|
|
25
|
+
return value;
|
|
26
|
+
if (typeof value === 'object' && '#text' in value)
|
|
27
|
+
return value['#text'];
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Transform raw Legacy XML topic to normalized XMindNode.
|
|
32
|
+
* Recursively processes child topics.
|
|
33
|
+
*/
|
|
34
|
+
export function transformLegacyTopic(legacyTopic) {
|
|
35
|
+
const node = {
|
|
36
|
+
id: legacyTopic['@_id'],
|
|
37
|
+
title: extractText(legacyTopic.title) || '',
|
|
38
|
+
children: [],
|
|
39
|
+
href: legacyTopic['@_href'],
|
|
40
|
+
};
|
|
41
|
+
// Extract note
|
|
42
|
+
const noteValue = legacyTopic.notes;
|
|
43
|
+
if (noteValue) {
|
|
44
|
+
node.note = extractText(noteValue);
|
|
45
|
+
}
|
|
46
|
+
// Extract markers
|
|
47
|
+
if (legacyTopic.markers && legacyTopic.markers.marker) {
|
|
48
|
+
const markerArray = normalizeArray(legacyTopic.markers.marker);
|
|
49
|
+
node.markers = markerArray.map(m => m['@_markerId']);
|
|
50
|
+
}
|
|
51
|
+
// Extract labels
|
|
52
|
+
if (legacyTopic.labels && legacyTopic.labels.label) {
|
|
53
|
+
const labelArray = normalizeArray(legacyTopic.labels.label);
|
|
54
|
+
node.labels = labelArray.map(l => extractText(l) || '').filter(l => l.length > 0);
|
|
55
|
+
}
|
|
56
|
+
// Recursively process child topics
|
|
57
|
+
// Support two formats:
|
|
58
|
+
// 1. Old format: <topic><topic>...</topic></topic> (direct topic children)
|
|
59
|
+
// 2. New format: <children><topics type="attached"><topic>...</topic></topics></children>
|
|
60
|
+
let childTopics = [];
|
|
61
|
+
if (legacyTopic.topic) {
|
|
62
|
+
// Old format: direct topic children
|
|
63
|
+
childTopics = normalizeArray(legacyTopic.topic);
|
|
64
|
+
}
|
|
65
|
+
else if (legacyTopic.children?.topics) {
|
|
66
|
+
// New format: children > topics array
|
|
67
|
+
const topicsArray = normalizeArray(legacyTopic.children.topics);
|
|
68
|
+
// Each topics element can have topic children
|
|
69
|
+
topicsArray.forEach(topicsElement => {
|
|
70
|
+
if (topicsElement.topic) {
|
|
71
|
+
const topicChildren = normalizeArray(topicsElement.topic);
|
|
72
|
+
childTopics.push(...topicChildren);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (childTopics.length > 0) {
|
|
77
|
+
node.children = childTopics.map(child => transformLegacyTopic(child));
|
|
78
|
+
}
|
|
79
|
+
return node;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Parse Legacy XML format content into XMindSheet objects.
|
|
83
|
+
*/
|
|
84
|
+
export async function parseLegacyContent(xmlText) {
|
|
85
|
+
try {
|
|
86
|
+
const parser = new XMLParser({
|
|
87
|
+
ignoreAttributes: false,
|
|
88
|
+
attributeNamePrefix: '@_',
|
|
89
|
+
textNodeName: '#text',
|
|
90
|
+
});
|
|
91
|
+
const parsed = parser.parse(xmlText);
|
|
92
|
+
if (!parsed['xmap-content']) {
|
|
93
|
+
throw new ParseError('Invalid Legacy XML format: missing xmap-content root element');
|
|
94
|
+
}
|
|
95
|
+
const xcontent = parsed['xmap-content'];
|
|
96
|
+
if (!xcontent.sheet) {
|
|
97
|
+
throw new ParseError('Invalid Legacy XML format: missing sheet element');
|
|
98
|
+
}
|
|
99
|
+
const sheets = normalizeArray(xcontent.sheet);
|
|
100
|
+
// Extract relationships from relationship-map (optional)
|
|
101
|
+
const relationshipsMap = new Map();
|
|
102
|
+
if (xcontent['relationship-map'] && xcontent['relationship-map'].relationship) {
|
|
103
|
+
const relArray = normalizeArray(xcontent['relationship-map'].relationship);
|
|
104
|
+
// Group relationships by sheet ID if available (relationships are usually global)
|
|
105
|
+
relArray.forEach(rel => {
|
|
106
|
+
if (!relationshipsMap.has('global')) {
|
|
107
|
+
relationshipsMap.set('global', []);
|
|
108
|
+
}
|
|
109
|
+
relationshipsMap.get('global').push(rel);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return sheets.map(sheet => {
|
|
113
|
+
if (!sheet['topic']) {
|
|
114
|
+
throw new ParseError(`Sheet "${sheet.title}" is missing root topic`);
|
|
115
|
+
}
|
|
116
|
+
debug('Transforming Legacy XML sheet', { sheetId: sheet['@_id'], title: sheet.title });
|
|
117
|
+
const rootTopic = transformLegacyTopic(sheet['topic']);
|
|
118
|
+
// Transform relationships for this sheet
|
|
119
|
+
const relationships = (relationshipsMap.get('global') || []).map(rel => ({
|
|
120
|
+
id: rel['@_id'],
|
|
121
|
+
title: rel['@_title'],
|
|
122
|
+
sourceId: rel['@_sourceId'],
|
|
123
|
+
targetId: rel['@_targetId'],
|
|
124
|
+
}));
|
|
125
|
+
return {
|
|
126
|
+
id: sheet['@_id'],
|
|
127
|
+
title: sheet.title,
|
|
128
|
+
rootTopic,
|
|
129
|
+
relationships: relationships.length > 0 ? relationships : undefined,
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err instanceof ParseError)
|
|
135
|
+
throw err;
|
|
136
|
+
throw new ParseError('Failed to parse Legacy XML', err.message);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=legacy-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-parser.js","sourceRoot":"","sources":["../../src/core/legacy-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main parser dispatcher for XMind files.
|
|
3
|
+
* Detects file format (Zen or Legacy) and routes to appropriate parser.
|
|
4
|
+
* Handles ZIP extraction, format detection, and unified result generation.
|
|
5
|
+
*/
|
|
6
|
+
import type { XMindParsedResult } from '../model/types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Parse an XMind file (.xmind is a ZIP container).
|
|
9
|
+
* Detects format (Zen JSON or Legacy XML) and routes to appropriate parser.
|
|
10
|
+
* Returns a unified XMindParsedResult with metadata.
|
|
11
|
+
*
|
|
12
|
+
* @param filePath - Absolute path to the .xmind file
|
|
13
|
+
* @returns Parsed XMind document with sheets and metadata
|
|
14
|
+
* @throws FileNotFoundError if file does not exist
|
|
15
|
+
* @throws InvalidFormatError if ZIP is corrupted or format cannot be detected
|
|
16
|
+
* @throws ParseError if content parsing fails
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseXMindFile(filePath: string): Promise<XMindParsedResult>;
|
|
19
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAa,iBAAiB,EAAc,MAAM,mBAAmB,CAAC;AAMlF;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+FjF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main parser dispatcher for XMind files.
|
|
3
|
+
* Detects file format (Zen or Legacy) and routes to appropriate parser.
|
|
4
|
+
* Handles ZIP extraction, format detection, and unified result generation.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
|
+
import JSZip from 'jszip';
|
|
9
|
+
import { FileNotFoundError, InvalidFormatError, ParseError } from '../utils/errors.js';
|
|
10
|
+
import { debug, info } from '../utils/logger.js';
|
|
11
|
+
import { parseZenContent, countTopics } from './zen-parser.js';
|
|
12
|
+
import { parseLegacyContent } from './legacy-parser.js';
|
|
13
|
+
/**
|
|
14
|
+
* Parse an XMind file (.xmind is a ZIP container).
|
|
15
|
+
* Detects format (Zen JSON or Legacy XML) and routes to appropriate parser.
|
|
16
|
+
* Returns a unified XMindParsedResult with metadata.
|
|
17
|
+
*
|
|
18
|
+
* @param filePath - Absolute path to the .xmind file
|
|
19
|
+
* @returns Parsed XMind document with sheets and metadata
|
|
20
|
+
* @throws FileNotFoundError if file does not exist
|
|
21
|
+
* @throws InvalidFormatError if ZIP is corrupted or format cannot be detected
|
|
22
|
+
* @throws ParseError if content parsing fails
|
|
23
|
+
*/
|
|
24
|
+
export async function parseXMindFile(filePath) {
|
|
25
|
+
info('Starting XMind file parsing', { filePath });
|
|
26
|
+
// Step 1: Verify file exists
|
|
27
|
+
try {
|
|
28
|
+
await fs.access(filePath);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
throw new FileNotFoundError(filePath);
|
|
32
|
+
}
|
|
33
|
+
// Step 2: Read file and extract ZIP
|
|
34
|
+
let zipData;
|
|
35
|
+
try {
|
|
36
|
+
zipData = await fs.readFile(filePath);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw new FileNotFoundError(filePath);
|
|
40
|
+
}
|
|
41
|
+
let zip;
|
|
42
|
+
try {
|
|
43
|
+
zip = new JSZip();
|
|
44
|
+
await zip.loadAsync(zipData);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
throw new InvalidFormatError('ZIP container is corrupted or invalid');
|
|
48
|
+
}
|
|
49
|
+
debug('ZIP loaded successfully', { fileSize: zipData.length });
|
|
50
|
+
// Step 3: Detect format (Zen or Legacy)
|
|
51
|
+
let sheets;
|
|
52
|
+
let formatVersion;
|
|
53
|
+
// Try Zen format first (content.json)
|
|
54
|
+
if (zip.file('content.json')) {
|
|
55
|
+
debug('Detected Zen format (content.json)');
|
|
56
|
+
formatVersion = 'zen';
|
|
57
|
+
try {
|
|
58
|
+
const jsonData = await zip.file('content.json').async('string');
|
|
59
|
+
sheets = await parseZenContent(jsonData);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw new ParseError(`Failed to parse Zen format: ${err.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (zip.file('content.xml')) {
|
|
66
|
+
// Fall back to Legacy format (content.xml)
|
|
67
|
+
debug('Detected Legacy format (content.xml)');
|
|
68
|
+
formatVersion = 'legacy';
|
|
69
|
+
try {
|
|
70
|
+
const xmlData = await zip.file('content.xml').async('string');
|
|
71
|
+
sheets = await parseLegacyContent(xmlData);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
throw new ParseError(`Failed to parse Legacy format: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Neither format detected
|
|
79
|
+
throw new InvalidFormatError('Neither content.json (Zen) nor content.xml (Legacy) found in ZIP. File may be corrupted or created with an unsupported XMind version.');
|
|
80
|
+
}
|
|
81
|
+
// Step 4: Validate sheets
|
|
82
|
+
if (!sheets || sheets.length === 0) {
|
|
83
|
+
throw new InvalidFormatError('No sheets found in parsed document');
|
|
84
|
+
}
|
|
85
|
+
debug('Sheets parsed successfully', { sheetCount: sheets.length });
|
|
86
|
+
// Step 5: Calculate total topic count across all sheets
|
|
87
|
+
let totalTopicCount = 0;
|
|
88
|
+
for (const sheet of sheets) {
|
|
89
|
+
const sheetTopicCount = countTopics(sheet.rootTopic);
|
|
90
|
+
totalTopicCount += sheetTopicCount;
|
|
91
|
+
debug('Sheet topic count', { sheetId: sheet.id, topicCount: sheetTopicCount });
|
|
92
|
+
}
|
|
93
|
+
// Step 6: Construct and return unified result
|
|
94
|
+
const result = {
|
|
95
|
+
filePath,
|
|
96
|
+
sheets,
|
|
97
|
+
meta: {
|
|
98
|
+
formatVersion,
|
|
99
|
+
topicCount: totalTopicCount,
|
|
100
|
+
parsedAt: new Date().toISOString(),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
info('XMind file parsed successfully', {
|
|
104
|
+
filePath,
|
|
105
|
+
formatVersion,
|
|
106
|
+
sheetCount: sheets.length,
|
|
107
|
+
topicCount: totalTopicCount,
|
|
108
|
+
});
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for modern XMind Zen format (content.json).
|
|
3
|
+
* Zen files use a straightforward JSON structure within the ZIP container.
|
|
4
|
+
*/
|
|
5
|
+
import type { XMindNode, XMindSheet } from '../model/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Parse Zen format JSON content into XMindSheet objects.
|
|
8
|
+
* Supports two formats: direct array of sheets or object with sheets property.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseZenContent(jsonText: string): Promise<XMindSheet[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Count total nodes in a parsed sheet (for metadata).
|
|
13
|
+
*/
|
|
14
|
+
export declare function countTopics(node: XMindNode): number;
|
|
15
|
+
//# sourceMappingURL=zen-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zen-parser.d.ts","sourceRoot":"","sources":["../../src/core/zen-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAuE/D;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmC7E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAMnD"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for modern XMind Zen format (content.json).
|
|
3
|
+
* Zen files use a straightforward JSON structure within the ZIP container.
|
|
4
|
+
*/
|
|
5
|
+
import { ParseError } from '../utils/errors.js';
|
|
6
|
+
import { debug } from '../utils/logger.js';
|
|
7
|
+
/**
|
|
8
|
+
* Transform raw Zen JSON topic to normalized XMindNode.
|
|
9
|
+
* Recursively processes children (both attached and detached).
|
|
10
|
+
*/
|
|
11
|
+
function transformZenTopic(zenTopic) {
|
|
12
|
+
const node = {
|
|
13
|
+
id: zenTopic.id,
|
|
14
|
+
title: zenTopic.title,
|
|
15
|
+
children: [],
|
|
16
|
+
labels: zenTopic.labels,
|
|
17
|
+
href: zenTopic.href,
|
|
18
|
+
};
|
|
19
|
+
// Extract note (can be string or object with format)
|
|
20
|
+
if (zenTopic.notes) {
|
|
21
|
+
if (typeof zenTopic.notes === 'string') {
|
|
22
|
+
node.note = zenTopic.notes;
|
|
23
|
+
}
|
|
24
|
+
else if (typeof zenTopic.notes === 'object' && 'content' in zenTopic.notes) {
|
|
25
|
+
node.note = zenTopic.notes.content;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Transform markers (extract marker IDs)
|
|
29
|
+
if (zenTopic.markers && zenTopic.markers.length > 0) {
|
|
30
|
+
node.markers = zenTopic.markers.map(m => m.markerId);
|
|
31
|
+
}
|
|
32
|
+
// Recursively process children (attached and detached)
|
|
33
|
+
if (zenTopic.children) {
|
|
34
|
+
const attached = zenTopic.children.attached || [];
|
|
35
|
+
const detached = zenTopic.children.detached || [];
|
|
36
|
+
const allChildren = [...attached, ...detached];
|
|
37
|
+
node.children = allChildren.map(child => transformZenTopic(child));
|
|
38
|
+
}
|
|
39
|
+
return node;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse Zen format JSON content into XMindSheet objects.
|
|
43
|
+
* Supports two formats: direct array of sheets or object with sheets property.
|
|
44
|
+
*/
|
|
45
|
+
export async function parseZenContent(jsonText) {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(jsonText);
|
|
48
|
+
let sheets;
|
|
49
|
+
// Handle both formats:
|
|
50
|
+
// 1. Direct array: [{ id, title, rootTopic }, ...]
|
|
51
|
+
// 2. Object with sheets: { sheets: [{ id, title, rootTopic }, ...] }
|
|
52
|
+
if (Array.isArray(parsed)) {
|
|
53
|
+
sheets = parsed;
|
|
54
|
+
}
|
|
55
|
+
else if (parsed.sheets && Array.isArray(parsed.sheets)) {
|
|
56
|
+
sheets = parsed.sheets;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw new ParseError('Invalid Zen format: missing or malformed sheets array');
|
|
60
|
+
}
|
|
61
|
+
return sheets.map(sheet => {
|
|
62
|
+
if (!sheet.rootTopic) {
|
|
63
|
+
throw new ParseError(`Sheet "${sheet.title}" is missing rootTopic`);
|
|
64
|
+
}
|
|
65
|
+
debug('Transforming Zen sheet', { sheetId: sheet.id, title: sheet.title });
|
|
66
|
+
return {
|
|
67
|
+
id: sheet.id,
|
|
68
|
+
title: sheet.title,
|
|
69
|
+
rootTopic: transformZenTopic(sheet.rootTopic),
|
|
70
|
+
relationships: sheet.relationships,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
if (err instanceof ParseError)
|
|
76
|
+
throw err;
|
|
77
|
+
throw new ParseError('Failed to parse Zen JSON', err.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Count total nodes in a parsed sheet (for metadata).
|
|
82
|
+
*/
|
|
83
|
+
export function countTopics(node) {
|
|
84
|
+
let count = 1;
|
|
85
|
+
for (const child of node.children) {
|
|
86
|
+
count += countTopics(child);
|
|
87
|
+
}
|
|
88
|
+
return count;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=zen-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zen-parser.js","sourceRoot":"","sources":["../../src/core/zen-parser.ts"],"names":[],"mappings":"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"}
|