sloth-d2c-mcp 1.0.4-beta70 → 1.0.4-beta71

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.
@@ -0,0 +1,697 @@
1
+ /**
2
+ * iOS平台适配器 - 支持SwiftUI/UIKit
3
+ */
4
+ import { BasePlatformAdapter } from './base-adapter.js';
5
+ import * as path from 'path';
6
+ export class IOSPlatformAdapter extends BasePlatformAdapter {
7
+ platform = 'ios';
8
+ async scanComponents(projectPath, options) {
9
+ const components = [];
10
+ // 扫描SwiftUI组件
11
+ const swiftUIComponents = await this.scanSwiftUIComponents(projectPath, options);
12
+ components.push(...swiftUIComponents);
13
+ // 扫描UIKit组件
14
+ const uiKitComponents = await this.scanUIKitComponents(projectPath, options);
15
+ components.push(...uiKitComponents);
16
+ // 扫描Objective-C组件
17
+ const objcComponents = await this.scanObjectiveCComponents(projectPath, options);
18
+ components.push(...objcComponents);
19
+ return this.deduplicateComponents(components);
20
+ }
21
+ /**
22
+ * 扫描SwiftUI组件
23
+ */
24
+ async scanSwiftUIComponents(projectPath, options) {
25
+ const components = [];
26
+ const files = await this.findFiles(projectPath, ['.swift'], options);
27
+ console.log(`[scanSwiftUIComponents] 找到 ${files.length} 个 Swift 文件`);
28
+ // 只扫描路径中包含 "component" 或 "view" 的文件(不区分大小写)
29
+ const componentFiles = files.filter((file) => {
30
+ const normalizedPath = file.toLowerCase();
31
+ return (normalizedPath.includes('component') ||
32
+ normalizedPath.includes('view') ||
33
+ normalizedPath.includes('screen'));
34
+ });
35
+ console.log(`[scanSwiftUIComponents] 过滤后,${componentFiles.length} 个文件在相关目录中`);
36
+ for (const file of componentFiles) {
37
+ try {
38
+ const content = await this.readFile(file);
39
+ const extractedComponents = this.extractSwiftUIComponents(file, content, projectPath);
40
+ console.log(`[scanSwiftUIComponents] 文件 ${file} 提取到 ${extractedComponents.length} 个组件`);
41
+ components.push(...extractedComponents);
42
+ }
43
+ catch (error) {
44
+ console.warn(`解析SwiftUI组件失败 ${file}:`, error);
45
+ }
46
+ }
47
+ return components;
48
+ }
49
+ /**
50
+ * 从文件内容中提取SwiftUI组件
51
+ */
52
+ extractSwiftUIComponents(filePath, content, projectPath) {
53
+ const relativePath = path.relative(projectPath, filePath).replace(/\\/g, '/');
54
+ const components = [];
55
+ // 匹配SwiftUI View结构体:
56
+ // struct ComponentName: View { ... }
57
+ // public struct ComponentName: View { ... }
58
+ // struct ComponentName: View, SomeProtocol { ... }
59
+ const swiftUIViewRegex = /(?:public\s+|private\s+|internal\s+)?struct\s+([A-Z][a-zA-Z0-9]*)\s*:\s*View(?:\s*,\s*[^{]+)?\s*\{/g;
60
+ // 匹配PreviewProvider (用于预览)
61
+ const previewProviderRegex = /struct\s+([A-Z][a-zA-Z0-9]*)Preview\s*:\s*PreviewProvider\s*\{/g;
62
+ let match;
63
+ // 提取SwiftUI View组件
64
+ while ((match = swiftUIViewRegex.exec(content)) !== null) {
65
+ const componentName = match[1];
66
+ const props = this.extractSwiftProps(content, componentName);
67
+ const metadata = this.extractSwiftMetadata(content, componentName);
68
+ components.push({
69
+ id: this.generateComponentId(filePath, componentName),
70
+ name: componentName,
71
+ path: relativePath,
72
+ platform: 'ios',
73
+ type: this.inferComponentType(componentName),
74
+ props,
75
+ metadata: {
76
+ ...metadata,
77
+ framework: 'SwiftUI',
78
+ },
79
+ });
80
+ }
81
+ // 提取PreviewProvider (通常包含主组件的引用)
82
+ while ((match = previewProviderRegex.exec(content)) !== null) {
83
+ const previewName = match[1];
84
+ // 尝试从Preview中找到实际组件名
85
+ const previewContent = this.extractPreviewContent(content, previewName);
86
+ if (previewContent) {
87
+ const actualComponentName = previewContent;
88
+ // 检查是否已经添加过
89
+ if (!components.find((c) => c.name === actualComponentName)) {
90
+ const props = this.extractSwiftProps(content, actualComponentName);
91
+ const metadata = this.extractSwiftMetadata(content, actualComponentName);
92
+ components.push({
93
+ id: this.generateComponentId(filePath, actualComponentName),
94
+ name: actualComponentName,
95
+ path: relativePath,
96
+ platform: 'ios',
97
+ type: this.inferComponentType(actualComponentName),
98
+ props,
99
+ metadata: {
100
+ ...metadata,
101
+ framework: 'SwiftUI',
102
+ },
103
+ });
104
+ }
105
+ }
106
+ }
107
+ console.log(`[extractSwiftUIComponents] 从 ${filePath} 提取到 ${components.length} 个组件:`, components.map((c) => c.name));
108
+ return components;
109
+ }
110
+ /**
111
+ * 扫描UIKit组件
112
+ */
113
+ async scanUIKitComponents(projectPath, options) {
114
+ const components = [];
115
+ const files = await this.findFiles(projectPath, ['.swift'], options);
116
+ // 扫描UIKit相关的文件
117
+ const uiKitFiles = files.filter((file) => {
118
+ const normalizedPath = file.toLowerCase();
119
+ return (normalizedPath.includes('uiview') ||
120
+ normalizedPath.includes('uiviewcontroller') ||
121
+ normalizedPath.includes('uicontrol'));
122
+ });
123
+ console.log(`[scanUIKitComponents] 过滤后,${uiKitFiles.length} 个UIKit相关文件`);
124
+ for (const file of uiKitFiles) {
125
+ try {
126
+ const content = await this.readFile(file);
127
+ const extractedComponents = this.extractUIKitComponents(file, content, projectPath);
128
+ components.push(...extractedComponents);
129
+ }
130
+ catch (error) {
131
+ console.warn(`解析UIKit组件失败 ${file}:`, error);
132
+ }
133
+ }
134
+ return components;
135
+ }
136
+ /**
137
+ * 从文件内容中提取UIKit组件
138
+ */
139
+ extractUIKitComponents(filePath, content, projectPath) {
140
+ const relativePath = path.relative(projectPath, filePath).replace(/\\/g, '/');
141
+ const components = [];
142
+ // 匹配UIKit类:
143
+ // class ComponentName: UIView { ... }
144
+ // class ComponentName: UIViewController { ... }
145
+ // class ComponentName: UIControl { ... }
146
+ const uiKitClassRegex = /(?:public\s+|private\s+|internal\s+)?class\s+([A-Z][a-zA-Z0-9]*)\s*:\s*(UIView|UIViewController|UIControl|UIButton|UILabel|UITextField|UITextView)(?:\s*,\s*[^{]+)?\s*\{/g;
147
+ let match;
148
+ while ((match = uiKitClassRegex.exec(content)) !== null) {
149
+ const componentName = match[1];
150
+ const baseClass = match[2];
151
+ const props = this.extractSwiftProps(content, componentName);
152
+ const metadata = this.extractSwiftMetadata(content, componentName);
153
+ components.push({
154
+ id: this.generateComponentId(filePath, componentName),
155
+ name: componentName,
156
+ path: relativePath,
157
+ platform: 'ios',
158
+ type: this.inferComponentType(componentName),
159
+ props,
160
+ metadata: {
161
+ ...metadata,
162
+ framework: 'UIKit',
163
+ // baseClass stored in description or tags if needed
164
+ description: metadata.description || `${baseClass} component`,
165
+ },
166
+ });
167
+ }
168
+ return components;
169
+ }
170
+ /**
171
+ * 扫描Objective-C组件
172
+ */
173
+ async scanObjectiveCComponents(projectPath, options) {
174
+ const components = [];
175
+ const headerFiles = await this.findFiles(projectPath, ['.h'], options);
176
+ const implFiles = await this.findFiles(projectPath, ['.m'], options);
177
+ console.log(`[scanObjectiveCComponents] 找到 ${headerFiles.length} 个 .h 文件和 ${implFiles.length} 个 .m 文件`);
178
+ // 创建文件映射:.h 和 .m 文件配对
179
+ const fileMap = new Map();
180
+ // 处理头文件
181
+ for (const headerFile of headerFiles) {
182
+ const baseName = path.basename(headerFile, '.h');
183
+ const dir = path.dirname(headerFile);
184
+ const implFile = path.join(dir, `${baseName}.m`);
185
+ if (!fileMap.has(baseName)) {
186
+ fileMap.set(baseName, {});
187
+ }
188
+ fileMap.get(baseName).header = headerFile;
189
+ }
190
+ // 处理实现文件
191
+ for (const implFile of implFiles) {
192
+ const baseName = path.basename(implFile, '.m');
193
+ const dir = path.dirname(implFile);
194
+ const headerFile = path.join(dir, `${baseName}.h`);
195
+ if (!fileMap.has(baseName)) {
196
+ fileMap.set(baseName, {});
197
+ }
198
+ fileMap.get(baseName).impl = implFile;
199
+ }
200
+ // 只扫描路径中包含 "component"、"view"、"cell" 的文件(不区分大小写)
201
+ const componentFiles = Array.from(fileMap.entries()).filter(([baseName, files]) => {
202
+ const normalizedName = baseName.toLowerCase();
203
+ const normalizedPath = (files.header || files.impl || '').toLowerCase();
204
+ return (normalizedName.includes('component') ||
205
+ normalizedName.includes('view') ||
206
+ normalizedName.includes('cell') ||
207
+ normalizedName.includes('controller') ||
208
+ normalizedPath.includes('view') ||
209
+ normalizedPath.includes('cell') ||
210
+ normalizedPath.includes('component'));
211
+ });
212
+ console.log(`[scanObjectiveCComponents] 过滤后,${componentFiles.length} 个相关文件`);
213
+ for (const [baseName, files] of componentFiles) {
214
+ try {
215
+ const headerContent = files.header ? await this.readFile(files.header) : '';
216
+ const implContent = files.impl ? await this.readFile(files.impl) : '';
217
+ const combinedContent = `${headerContent}\n${implContent}`;
218
+ const extractedComponents = this.extractObjectiveCComponents(files.header || files.impl || '', headerContent, implContent, combinedContent, projectPath);
219
+ console.log(`[scanObjectiveCComponents] 文件 ${baseName} 提取到 ${extractedComponents.length} 个组件`);
220
+ components.push(...extractedComponents);
221
+ }
222
+ catch (error) {
223
+ console.warn(`解析Objective-C组件失败 ${baseName}:`, error);
224
+ }
225
+ }
226
+ return components;
227
+ }
228
+ /**
229
+ * 从文件内容中提取Objective-C组件
230
+ */
231
+ extractObjectiveCComponents(filePath, headerContent, implContent, combinedContent, projectPath) {
232
+ const relativePath = path.relative(projectPath, filePath).replace(/\\/g, '/');
233
+ const components = [];
234
+ // 匹配 @interface 声明:
235
+ // @interface ComponentName : SuperClass
236
+ // @interface ComponentName (Category)
237
+ const interfaceRegex = /@interface\s+([A-Z][a-zA-Z0-9]*)\s*(?::\s*([A-Z][a-zA-Z0-9_]*))?/g;
238
+ let match;
239
+ while ((match = interfaceRegex.exec(headerContent)) !== null) {
240
+ const componentName = match[1];
241
+ const superClass = match[2] || 'NSObject';
242
+ // 跳过分类(Category)和扩展(Extension)
243
+ if (headerContent.includes(`@interface ${componentName} (`)) {
244
+ continue;
245
+ }
246
+ const props = this.extractObjCProps(headerContent, componentName);
247
+ const metadata = this.extractObjCMetadata(headerContent, implContent, componentName, superClass);
248
+ components.push({
249
+ id: this.generateComponentId(filePath, componentName),
250
+ name: componentName,
251
+ path: relativePath,
252
+ platform: 'ios',
253
+ type: this.inferComponentType(componentName),
254
+ props,
255
+ metadata: {
256
+ ...metadata,
257
+ framework: 'UIKit',
258
+ },
259
+ });
260
+ }
261
+ return components;
262
+ }
263
+ /**
264
+ * 提取Objective-C组件的属性
265
+ */
266
+ extractObjCProps(content, componentName) {
267
+ const props = [];
268
+ // 匹配 @property 声明:
269
+ // @property (nonatomic, strong) Type *propertyName;
270
+ // @property (nonatomic, weak) id<Protocol> propertyName;
271
+ // @property (nonatomic, assign) NSInteger propertyName;
272
+ const propertyRegex = /@property\s*\([^)]+\)\s+(?:IBOutlet\s+)?(?:IBAction\s+)?(?:[\w<>,\s*]+)?\s*(\w+)\s*;/g;
273
+ let match;
274
+ while ((match = propertyRegex.exec(content)) !== null) {
275
+ const propName = match[1];
276
+ // 跳过私有属性(以_开头)和系统属性
277
+ if (propName.startsWith('_') || propName === 'delegate' || propName === 'dataSource') {
278
+ continue;
279
+ }
280
+ // 尝试提取类型信息
281
+ const propertyLine = this.extractPropertyLine(content, propName);
282
+ const typeInfo = this.parseObjCPropertyType(propertyLine);
283
+ props.push({
284
+ name: propName,
285
+ type: typeInfo.type,
286
+ required: typeInfo.required,
287
+ description: typeInfo.description,
288
+ });
289
+ }
290
+ return props;
291
+ }
292
+ /**
293
+ * 提取属性声明行
294
+ */
295
+ extractPropertyLine(content, propName) {
296
+ const lines = content.split('\n');
297
+ for (let i = 0; i < lines.length; i++) {
298
+ if (lines[i].includes(`@property`) && lines[i].includes(propName)) {
299
+ // 可能跨多行
300
+ let fullLine = lines[i];
301
+ let j = i + 1;
302
+ while (j < lines.length && !lines[j].includes(';')) {
303
+ fullLine += ' ' + lines[j].trim();
304
+ j++;
305
+ }
306
+ if (fullLine.includes(';')) {
307
+ return fullLine;
308
+ }
309
+ }
310
+ }
311
+ return '';
312
+ }
313
+ /**
314
+ * 解析Objective-C属性类型
315
+ */
316
+ parseObjCPropertyType(propertyLine) {
317
+ // 提取类型
318
+ let type = 'id';
319
+ // 匹配 strong/retain/weak/assign/copy
320
+ const isStrong = propertyLine.includes('strong') || propertyLine.includes('retain');
321
+ const isWeak = propertyLine.includes('weak');
322
+ const isCopy = propertyLine.includes('copy');
323
+ const isAssign = propertyLine.includes('assign');
324
+ // 尝试提取具体类型
325
+ const typeMatch = propertyLine.match(/@property\s*\([^)]+\)\s+(?:IBOutlet\s+)?(?:IBAction\s+)?([\w<>,\s*]+?)\s+\w+\s*;/);
326
+ if (typeMatch) {
327
+ type = typeMatch[1].trim();
328
+ // 清理类型字符串
329
+ type = type.replace(/\s+/g, ' ');
330
+ // 处理指针类型
331
+ if (type.includes('*')) {
332
+ type = type.replace(/\s*\*\s*/g, ' *');
333
+ }
334
+ }
335
+ // 提取注释
336
+ const commentMatch = propertyLine.match(/\/\/\s*(.+)/);
337
+ const description = commentMatch ? commentMatch[1].trim() : undefined;
338
+ return {
339
+ type: type || 'id',
340
+ required: isStrong || isCopy, // strong/copy 通常表示必需
341
+ description,
342
+ };
343
+ }
344
+ /**
345
+ * 提取Objective-C组件元数据
346
+ */
347
+ extractObjCMetadata(headerContent, implContent, componentName, superClass) {
348
+ const metadata = {
349
+ tags: [],
350
+ dependencies: [],
351
+ styleProps: {},
352
+ };
353
+ const combinedContent = `${headerContent}\n${implContent}`;
354
+ // 提取文档注释
355
+ const docCommentRegex = new RegExp(`//\\s*([^\\n]+)|/\\*\\*([^*]|\\*(?!/))*\\*/\\s*@interface\\s+${componentName}`, 's');
356
+ const docCommentMatch = combinedContent.match(docCommentRegex);
357
+ if (docCommentMatch) {
358
+ const docText = docCommentMatch[0];
359
+ const descMatch = docText.match(/(?:\/\/\s*|\/\*\*\s*)([^@\n*]+)/);
360
+ if (descMatch) {
361
+ metadata.description = descMatch[1].trim();
362
+ }
363
+ }
364
+ // 提取 #import 依赖
365
+ const importRegex = /#import\s+[<"]([^>"]+)[>"]/g;
366
+ let importMatch;
367
+ while ((importMatch = importRegex.exec(combinedContent)) !== null) {
368
+ metadata.dependencies.push(importMatch[1]);
369
+ }
370
+ // 提取样式相关的属性(UIView相关)
371
+ metadata.styleProps = this.extractObjCStyleProps(combinedContent);
372
+ // 添加基类信息到描述
373
+ if (superClass && superClass !== 'NSObject') {
374
+ metadata.description = metadata.description
375
+ ? `${metadata.description} (继承自 ${superClass})`
376
+ : `继承自 ${superClass}`;
377
+ }
378
+ return metadata;
379
+ }
380
+ /**
381
+ * 提取Objective-C样式属性
382
+ */
383
+ extractObjCStyleProps(content) {
384
+ const styleProps = {};
385
+ // 查找常见的样式属性设置
386
+ const stylePatterns = [
387
+ { key: 'width', pattern: /\.width\s*=\s*(\d+(?:\.\d+)?)/i },
388
+ { key: 'height', pattern: /\.height\s*=\s*(\d+(?:\.\d+)?)/i },
389
+ { key: 'frame', pattern: /CGRectMake\(([^)]+)\)/i },
390
+ { key: 'backgroundColor', pattern: /\.backgroundColor\s*=\s*\[UIColor\s+(\w+)\]/i },
391
+ { key: 'fontSize', pattern: /fontWithSize:(\d+(?:\.\d+)?)/i },
392
+ ];
393
+ for (const { key, pattern } of stylePatterns) {
394
+ const match = content.match(pattern);
395
+ if (match) {
396
+ styleProps[key] = match[1] || 'defined';
397
+ }
398
+ }
399
+ return styleProps;
400
+ }
401
+ /**
402
+ * 提取Swift组件的属性
403
+ */
404
+ extractSwiftProps(content, componentName) {
405
+ const props = [];
406
+ // 匹配Swift属性:
407
+ // @State var propertyName: Type
408
+ // @Binding var propertyName: Type
409
+ // @Published var propertyName: Type
410
+ // var propertyName: Type
411
+ // let propertyName: Type
412
+ const propertyRegex = new RegExp(`(?:@(?:State|Binding|Published|ObservedObject|Environment|EnvironmentObject)\\s+)?(?:var|let)\\s+(\\w+)\\??\\s*:\\s*([^=\\n]+?)(?:\\s*=|\\s*\\{|\\s*\\()`, 'g');
413
+ // 匹配初始化参数:
414
+ // init(propertyName: Type, ...)
415
+ const initRegex = new RegExp(`(?:public\\s+|private\\s+|internal\\s+)?init\\s*\\(([^)]+)\\)`, 's');
416
+ let match;
417
+ // 提取属性
418
+ while ((match = propertyRegex.exec(content)) !== null) {
419
+ const propName = match[1];
420
+ const propType = match[2].trim();
421
+ const isOptional = propType.includes('?');
422
+ const cleanType = propType.replace(/\?/g, '').trim();
423
+ // 跳过私有属性(以_开头)
424
+ if (propName.startsWith('_'))
425
+ continue;
426
+ props.push({
427
+ name: propName,
428
+ type: cleanType,
429
+ required: !isOptional,
430
+ });
431
+ }
432
+ // 提取初始化参数
433
+ const initMatch = content.match(initRegex);
434
+ if (initMatch) {
435
+ const paramsStr = initMatch[1];
436
+ const paramRegex = /(\w+)(\??)\s*:\s*([^,)]+)/g;
437
+ let paramMatch;
438
+ while ((paramMatch = paramRegex.exec(paramsStr)) !== null) {
439
+ const paramName = paramMatch[1];
440
+ const isOptional = paramMatch[2] === '?';
441
+ const paramType = paramMatch[3].trim().replace(/\?/g, '');
442
+ // 检查是否已经存在(避免重复)
443
+ if (!props.find((p) => p.name === paramName)) {
444
+ props.push({
445
+ name: paramName,
446
+ type: paramType,
447
+ required: !isOptional,
448
+ });
449
+ }
450
+ }
451
+ }
452
+ return props;
453
+ }
454
+ /**
455
+ * 提取Swift组件元数据
456
+ */
457
+ extractSwiftMetadata(content, componentName) {
458
+ const metadata = {
459
+ tags: [],
460
+ dependencies: [],
461
+ styleProps: {},
462
+ };
463
+ // 提取文档注释
464
+ const docCommentRegex = new RegExp(`///\\s*([^\\n]+)|/\\*\\*([^*]|\\*(?!/))*\\*/\\s*struct\\s+${componentName}|/\\*\\*([^*]|\\*(?!/))*\\*/\\s*class\\s+${componentName}`, 's');
465
+ const docCommentMatch = content.match(docCommentRegex);
466
+ if (docCommentMatch) {
467
+ const docText = docCommentMatch[0];
468
+ // Match single-line comment (///) or multi-line comment (/**)
469
+ const descMatch = docText.match(/(?:\/\/\/\s*|\/\*\*\s*)([^@\n*]+)/);
470
+ if (descMatch) {
471
+ metadata.description = descMatch[1].trim();
472
+ }
473
+ // 提取@标签
474
+ const tagRegex = /@(\w+)\s+([^\n]*)/g;
475
+ let tagMatch;
476
+ while ((tagMatch = tagRegex.exec(docText)) !== null) {
477
+ metadata.tags.push(tagMatch[1]);
478
+ }
479
+ }
480
+ // 提取import依赖
481
+ const importRegex = /import\s+([^\n]+)/g;
482
+ let importMatch;
483
+ while ((importMatch = importRegex.exec(content)) !== null) {
484
+ metadata.dependencies.push(importMatch[1].trim());
485
+ }
486
+ // 提取样式相关的属性
487
+ metadata.styleProps = this.extractSwiftStyleProps(content);
488
+ return metadata;
489
+ }
490
+ /**
491
+ * 提取Swift样式属性
492
+ */
493
+ extractSwiftStyleProps(content) {
494
+ const styleProps = {};
495
+ // 查找常见的样式属性(SwiftUI modifier)
496
+ const stylePatterns = [
497
+ { key: 'width', pattern: /\.frame\([^)]*width:\s*(\d+(?:\.\d+)?)/i },
498
+ { key: 'height', pattern: /\.frame\([^)]*height:\s*(\d+(?:\.\d+)?)/i },
499
+ { key: 'padding', pattern: /\.padding\(([^)]+)\)/i },
500
+ { key: 'backgroundColor', pattern: /\.background\([^)]*Color\([^)]*\)/i },
501
+ { key: 'borderRadius', pattern: /\.cornerRadius\((\d+(?:\.\d+)?)/i },
502
+ { key: 'fontSize', pattern: /\.font\([^)]*\.system\(size:\s*(\d+(?:\.\d+)?)/i },
503
+ { key: 'fontWeight', pattern: /\.fontWeight\(\.(\w+)\)/i },
504
+ { key: 'color', pattern: /\.foregroundColor\([^)]*Color\([^)]*\)/i },
505
+ ];
506
+ for (const { key, pattern } of stylePatterns) {
507
+ const match = content.match(pattern);
508
+ if (match) {
509
+ styleProps[key] = match[1] || 'defined';
510
+ }
511
+ }
512
+ return styleProps;
513
+ }
514
+ /**
515
+ * 从Preview中提取实际组件名
516
+ */
517
+ extractPreviewContent(content, previewName) {
518
+ const previewRegex = new RegExp(`struct\\s+${previewName}Preview[\\s\\S]*?static\\s+var\\s+previews[\\s\\S]*?return\\s+([A-Z][a-zA-Z0-9]*)\\(`, 's');
519
+ const match = content.match(previewRegex);
520
+ return match ? match[1] : null;
521
+ }
522
+ async parseComponentMetadata(filePath) {
523
+ const content = await this.readFile(filePath);
524
+ // 尝试提取Swift组件名
525
+ const structMatch = content.match(/struct\s+([A-Z][a-zA-Z0-9]*)\s*:\s*View/);
526
+ const classMatch = content.match(/class\s+([A-Z][a-zA-Z0-9]*)\s*:\s*(UIView|UIViewController)/);
527
+ if (structMatch || classMatch) {
528
+ const componentName = structMatch?.[1] || classMatch?.[1] || '';
529
+ if (componentName) {
530
+ return this.extractSwiftMetadata(content, componentName);
531
+ }
532
+ }
533
+ // 尝试提取Objective-C组件名
534
+ if (filePath.endsWith('.h') || filePath.endsWith('.m')) {
535
+ const interfaceMatch = content.match(/@interface\s+([A-Z][a-zA-Z0-9]*)\s*(?::\s*([A-Z][a-zA-Z0-9_]*))?/);
536
+ if (interfaceMatch) {
537
+ const componentName = interfaceMatch[1];
538
+ const superClass = interfaceMatch[2] || 'NSObject';
539
+ // 对于 .h 文件,需要读取对应的 .m 文件
540
+ let implContent = '';
541
+ if (filePath.endsWith('.h')) {
542
+ const implPath = filePath.replace(/\.h$/, '.m');
543
+ try {
544
+ implContent = await this.readFile(implPath);
545
+ }
546
+ catch {
547
+ // .m 文件不存在,只使用 .h 文件
548
+ }
549
+ }
550
+ else {
551
+ implContent = content;
552
+ }
553
+ return this.extractObjCMetadata(content, implContent, componentName, superClass);
554
+ }
555
+ }
556
+ return {
557
+ tags: [],
558
+ dependencies: [],
559
+ styleProps: {},
560
+ };
561
+ }
562
+ generateImportCode(component, targetPath) {
563
+ // Objective-C使用#import
564
+ if (component.path.endsWith('.h') || component.path.endsWith('.m')) {
565
+ if (!targetPath) {
566
+ const headerName = component.path.endsWith('.h')
567
+ ? path.basename(component.path)
568
+ : path.basename(component.path, '.m') + '.h';
569
+ return `#import "${headerName}"`;
570
+ }
571
+ // 计算相对路径
572
+ const targetDir = path.dirname(targetPath);
573
+ const componentDir = path.dirname(component.path);
574
+ const headerName = component.path.endsWith('.h')
575
+ ? path.basename(component.path)
576
+ : path.basename(component.path, '.m') + '.h';
577
+ // 如果在同一目录,直接导入文件名
578
+ if (targetDir === componentDir) {
579
+ return `#import "${headerName}"`;
580
+ }
581
+ // 否则使用相对路径(指向头文件)
582
+ const headerPath = component.path.endsWith('.h')
583
+ ? component.path
584
+ : component.path.replace(/\.m$/, '.h');
585
+ const relativePath = this.getRelativePath(targetPath, headerPath);
586
+ return `#import "${relativePath}"`;
587
+ }
588
+ // Swift使用import语句,但通常不需要相对路径导入
589
+ // 如果组件在同一个模块中,不需要import
590
+ // 如果组件在不同模块中,使用模块名
591
+ if (!targetPath) {
592
+ return `// 确保已导入组件所在模块\n// import ${component.metadata.framework || 'Foundation'}`;
593
+ }
594
+ // 检查是否在同一目录
595
+ const targetDir = path.dirname(targetPath);
596
+ const componentDir = path.dirname(component.path);
597
+ if (targetDir === componentDir) {
598
+ return `// 组件在同一文件中,无需导入`;
599
+ }
600
+ // 不同模块需要import(这里简化处理)
601
+ const moduleName = component.metadata.framework || 'Foundation';
602
+ return `import ${moduleName}`;
603
+ }
604
+ generateUsageCode(component, props, styles) {
605
+ // SwiftUI使用方式
606
+ if (component.metadata.framework === 'SwiftUI') {
607
+ const propsStr = Object.entries(props)
608
+ .map(([key, value]) => {
609
+ if (typeof value === 'string') {
610
+ return `${key}: "${value}"`;
611
+ }
612
+ else if (typeof value === 'boolean') {
613
+ return `${key}: ${value}`;
614
+ }
615
+ else if (typeof value === 'number') {
616
+ return `${key}: ${value}`;
617
+ }
618
+ else {
619
+ return `${key}: ${JSON.stringify(value)}`;
620
+ }
621
+ })
622
+ .join(', ');
623
+ // SwiftUI样式通过modifier应用
624
+ const styleModifiers = [];
625
+ if (styles.width)
626
+ styleModifiers.push(`.frame(width: ${styles.width})`);
627
+ if (styles.height)
628
+ styleModifiers.push(`.frame(height: ${styles.height})`);
629
+ if (styles.padding)
630
+ styleModifiers.push(`.padding(${styles.padding})`);
631
+ if (styles.backgroundColor)
632
+ styleModifiers.push(`.background(Color(${styles.backgroundColor}))`);
633
+ if (styles.borderRadius)
634
+ styleModifiers.push(`.cornerRadius(${styles.borderRadius})`);
635
+ if (styles.fontSize)
636
+ styleModifiers.push(`.font(.system(size: ${styles.fontSize}))`);
637
+ if (styles.color)
638
+ styleModifiers.push(`.foregroundColor(Color(${styles.color}))`);
639
+ const modifiersStr = styleModifiers.length > 0 ? '\n ' + styleModifiers.join('\n ') : '';
640
+ return propsStr
641
+ ? `${component.name}(${propsStr})${modifiersStr}`
642
+ : `${component.name}()${modifiersStr}`;
643
+ }
644
+ // UIKit Swift使用方式
645
+ if (component.metadata.framework === 'UIKit' && !component.path.endsWith('.h') && !component.path.endsWith('.m')) {
646
+ const propsStr = Object.entries(props)
647
+ .map(([key, value]) => {
648
+ if (typeof value === 'string') {
649
+ return `\n ${key} = "${value}"`;
650
+ }
651
+ else if (typeof value === 'boolean') {
652
+ return `\n ${key} = ${value}`;
653
+ }
654
+ else if (typeof value === 'number') {
655
+ return `\n ${key} = ${value}`;
656
+ }
657
+ else {
658
+ return `\n ${key} = ${JSON.stringify(value)}`;
659
+ }
660
+ })
661
+ .join(',');
662
+ return `let ${component.name.toLowerCase()} = ${component.name}()${propsStr}`;
663
+ }
664
+ // Objective-C使用方式
665
+ if (component.path.endsWith('.h') || component.path.endsWith('.m')) {
666
+ const varName = component.name.charAt(0).toLowerCase() + component.name.slice(1);
667
+ const propsStr = Object.entries(props)
668
+ .map(([key, value]) => {
669
+ if (typeof value === 'string') {
670
+ return `\n ${varName}.${key} = @"${value}";`;
671
+ }
672
+ else if (typeof value === 'boolean') {
673
+ return `\n ${varName}.${key} = ${value ? 'YES' : 'NO'};`;
674
+ }
675
+ else if (typeof value === 'number') {
676
+ return `\n ${varName}.${key} = ${value};`;
677
+ }
678
+ else {
679
+ return `\n ${varName}.${key} = ${JSON.stringify(value)};`;
680
+ }
681
+ })
682
+ .join('');
683
+ // 检查是否是UITableViewCell
684
+ if (component.name.includes('Cell')) {
685
+ return `${component.name} *${varName} = [[${component.name} alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"${varName}"];${propsStr}`;
686
+ }
687
+ // 检查是否是UIViewController
688
+ if (component.name.includes('Controller')) {
689
+ return `${component.name} *${varName} = [[${component.name} alloc] init];${propsStr}`;
690
+ }
691
+ // 默认UIView
692
+ return `${component.name} *${varName} = [[${component.name} alloc] init];${propsStr}`;
693
+ }
694
+ // 默认SwiftUI格式
695
+ return `${component.name}()`;
696
+ }
697
+ }