@appthen/cli 1.1.32 → 1.2.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/bin/main.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * 导入包信息,用于获取版本号
8
8
  */
9
9
  var pkg = require('../package.json');
10
+ var path = require('path');
10
11
 
11
12
  /**
12
13
  * 导入commander用于构建CLI命令行工具
@@ -14,8 +15,7 @@ var pkg = require('../package.json');
14
15
  var program = require('commander');
15
16
 
16
17
  // 设置版本信息命令
17
- program
18
- .version(pkg.version, '-v, --version', 'display version information');
18
+ program.version(pkg.version, '-v, --version', 'display version information');
19
19
 
20
20
  /**
21
21
  * generate命令 - 默认命令
@@ -208,12 +208,20 @@ program
208
208
  .description('Sync backend code from remote')
209
209
  .requiredOption('-p, --id <id>', 'project id')
210
210
  .requiredOption('-a, --auth <auth>', 'auth token')
211
- .option('-f, --framework <framework>', 'backend language framework', 'fastify')
211
+ .option(
212
+ '-f, --framework <framework>',
213
+ 'backend language framework',
214
+ 'fastify'
215
+ )
212
216
  .option('-o, --output <output>', 'specify the output directory', 'src/types')
213
217
  .option('-m, --mapping <mapping>', 'path mapping configuration')
214
218
  .option('-cc, --clear <clear>', 'clear cache')
215
219
  .option('-c, --cwd <cwd>', 'specify the working directory', '.')
216
- .option('-q, --quiet', 'be quiet, do not output anything unless get error', false)
220
+ .option(
221
+ '-q, --quiet',
222
+ 'be quiet, do not output anything unless get error',
223
+ false
224
+ )
217
225
  .option('-vb, --verbose', 'be verbose, output more information', false)
218
226
  .option('--nocheck', 'add @ts-nocheck to cloud function files', false)
219
227
  .action(function doSync(command) {
@@ -261,7 +269,7 @@ program
261
269
  // 处理环境变量
262
270
  const env = {};
263
271
  if (options.env) {
264
- options.env.forEach(item => {
272
+ options.env.forEach((item) => {
265
273
  const [key, value] = item.split('=');
266
274
  if (key && value) {
267
275
  env[key] = value;
@@ -276,7 +284,7 @@ program
276
284
  name: options.projectName,
277
285
  target: options.prod ? 'production' : 'preview',
278
286
  outputDir: options.output,
279
- env
287
+ env,
280
288
  };
281
289
 
282
290
  require('../dist/index.js')
@@ -313,6 +321,7 @@ program
313
321
  .option('-vb, --verbose', 'be verbose, output more information', false)
314
322
  .option('--daemon', 'run as daemon process', false)
315
323
  .option('--debug', 'debug mode, process will not exit automatically', false)
324
+ .option('--workspace', 'enable shadow space workspace features', false)
316
325
  .action(async function doConnect(command) {
317
326
  var options = command.opts();
318
327
  if (options.cwd) {
@@ -323,7 +332,8 @@ program
323
332
  const retCode = await require('../dist/index.js').startConnecting({
324
333
  ...options,
325
334
  daemon: options.daemon,
326
- debug: options.debug
335
+ debug: options.debug,
336
+ workspace: options.workspace,
327
337
  });
328
338
  if (!options.debug) {
329
339
  process.exit(retCode);
@@ -372,8 +382,13 @@ program
372
382
  */
373
383
  program
374
384
  .command('proxy')
375
- .description('start local proxy server to solve CORS and private network access')
376
- .requiredOption('--target <target>', 'target server address, e.g. http://localhost:3000')
385
+ .description(
386
+ 'start local proxy server to solve CORS and private network access'
387
+ )
388
+ .requiredOption(
389
+ '--target <target>',
390
+ 'target server address, e.g. http://localhost:3000'
391
+ )
377
392
  .requiredOption('--port <port>', 'local listen port, e.g. 9000')
378
393
  .option('--daemon', 'run as daemon process', false)
379
394
  .option('--debug', 'debug mode, process will not exit automatically', false)
@@ -381,9 +396,13 @@ program
381
396
  .on('--help', function () {
382
397
  console.log('\n用法示例:');
383
398
  console.log(' # 启动本地代理,将9000端口转发到目标服务');
384
- console.log(' $ yourcli proxy --target http://192.168.1.100:8080 --port 9000');
399
+ console.log(
400
+ ' $ yourcli proxy --target http://192.168.1.100:8080 --port 9000'
401
+ );
385
402
  console.log('\n # 以守护进程方式运行');
386
- console.log(' $ yourcli proxy --target http://192.168.1.100:8080 --port 9000 --daemon');
403
+ console.log(
404
+ ' $ yourcli proxy --target http://192.168.1.100:8080 --port 9000 --daemon'
405
+ );
387
406
  console.log('\n # 停止代理服务');
388
407
  console.log(' $ yourcli proxy --stop');
389
408
  console.log('\n参数说明:');
@@ -416,5 +435,204 @@ program
416
435
  });
417
436
  });
418
437
 
419
- // 解析命令行参数
438
+ /**
439
+ * shadow-space命令
440
+ * 影子空间调试工具
441
+ */
442
+ program
443
+ .command('shadow-space')
444
+ .description('shadow space debugging tool')
445
+ .option(
446
+ '-a, --operation <operation>',
447
+ 'operation to perform: init|info|changes|sync|reset|cleanup',
448
+ 'info'
449
+ )
450
+ .option('-r, --project-root <path>', 'project root directory', '.')
451
+ .option('-i, --project-id <id>', 'project ID', 'debug-project')
452
+ .option('-u, --user-id <id>', 'user ID', 'debug-user')
453
+ .option('-s, --space-id <id>', 'space ID', 'debug')
454
+ .option('-f, --files <files>', 'comma-separated list of files to sync')
455
+ .option('-v, --verbose', 'verbose output', false)
456
+ .on('--help', function () {
457
+ console.log('\n用法示例:');
458
+ console.log(' # 初始化影子空间');
459
+ console.log(
460
+ ' $ appthen shadow-space --operation init --project-id my-project --user-id user123'
461
+ );
462
+ console.log('\n # 查看空间信息');
463
+ console.log(' $ appthen shadow-space --operation info');
464
+ console.log('\n # 检测文件变更');
465
+ console.log(' $ appthen shadow-space --operation changes --verbose');
466
+ console.log('\n # 同步指定文件');
467
+ console.log(
468
+ ' $ appthen shadow-space --operation sync --files "src/index.ts,package.json"'
469
+ );
470
+ console.log('\n # 重置影子空间');
471
+ console.log(' $ appthen shadow-space --operation reset');
472
+ console.log('\n # 清理影子空间');
473
+ console.log(' $ appthen shadow-space --operation cleanup');
474
+ console.log('\n支持的操作:');
475
+ console.log(' init - 初始化影子空间');
476
+ console.log(' info - 显示空间信息');
477
+ console.log(' changes - 检测文件变更');
478
+ console.log(' sync - 同步文件到影子空间');
479
+ console.log(' reset - 重置影子空间');
480
+ console.log(' cleanup - 清理影子空间');
481
+ })
482
+ .action(async function doShadowSpace(command) {
483
+ const options = command.opts();
484
+
485
+ try {
486
+ const { executeShadowSpaceDebug } = require('../dist/index.js');
487
+
488
+ await executeShadowSpaceDebug({
489
+ projectRoot: path.resolve(options.projectRoot),
490
+ projectId: options.projectId,
491
+ userId: options.userId,
492
+ spaceId: options.spaceId,
493
+ action: options.operation,
494
+ files: options.files
495
+ ? options.files.split(',').map((f) => f.trim())
496
+ : undefined,
497
+ verbose: options.verbose,
498
+ });
499
+ } catch (error) {
500
+ console.error('Shadow space command failed:', error);
501
+ process.exit(1);
502
+ }
503
+ });
504
+
505
+ /**
506
+ * check-tsx命令
507
+ * 检查 TSX 文件是否符合平台规范
508
+ * 可选参数:
509
+ * -f, --files: 要检查的文件模式,默认为所有 tsx 文件
510
+ * -c, --cwd: 指定工作目录
511
+ * -r, --recursive: 递归检查子目录
512
+ * -e, --errors-only: 只显示错误
513
+ * -v, --verbose: 详细输出
514
+ * --format: 输出格式 (text|json)
515
+ * --exit-on-error: 发现错误时退出
516
+ */
517
+ program
518
+ .command('check-tsx')
519
+ .description('Check TSX files compliance with platform standards')
520
+ .option('-f, --files <files...>', 'file patterns to check')
521
+ .option('-c, --cwd <cwd>', 'specify the working directory', '.')
522
+ .option('-r, --recursive', 'recursively check subdirectories', true)
523
+ .option('-e, --errors-only', 'only show errors', false)
524
+ .option('-v, --verbose', 'verbose output', false)
525
+ .option('--format <format>', 'output format (text|json)', 'text')
526
+ .option('--exit-on-error', 'exit with error code if issues found', false)
527
+ .on('--help', function () {
528
+ console.log('\n用法示例:');
529
+ console.log(' # 检查当前目录下所有 TSX 文件');
530
+ console.log(' $ appthen check-tsx');
531
+ console.log('\n # 检查指定文件');
532
+ console.log(' $ appthen check-tsx -f "src/**/*.tsx"');
533
+ console.log('\n # 只显示错误,不显示警告');
534
+ console.log(' $ appthen check-tsx --errors-only');
535
+ console.log('\n # 输出 JSON 格式结果');
536
+ console.log(' $ appthen check-tsx --format json');
537
+ console.log('\n # 发现错误时退出(用于 CI/CD)');
538
+ console.log(' $ appthen check-tsx --exit-on-error');
539
+ console.log('\n检查规则:');
540
+ console.log(' - 文件结构完整性(JSDoc、IProps、IState、Document 类)');
541
+ console.log(' - render() 方法约束(无变量声明、无逻辑语句)');
542
+ console.log(' - TypeScript 语法限制(方法中禁用类型注解)');
543
+ console.log(' - JSX 语法限制(只有 render() 方法可以包含 JSX)');
544
+ console.log(' - 对象安全访问(推荐使用可选链)');
545
+ })
546
+ .action(async function doCheckTSX(command) {
547
+ const options = command.opts();
548
+ if (options.cwd) {
549
+ process.chdir(options.cwd);
550
+ }
551
+
552
+ try {
553
+ const { checkTSX } = require('../dist/index.js');
554
+
555
+ const result = await checkTSX({
556
+ files: options.files || ['**/*.tsx'], // 设置默认值
557
+ cwd: options.cwd,
558
+ recursive: options.recursive,
559
+ errorsOnly: options.errorsOnly,
560
+ verbose: options.verbose,
561
+ format: options.format,
562
+ exitOnError: options.exitOnError,
563
+ });
564
+
565
+ // 如果是 JSON 格式,不需要额外处理,checkTSX 已经输出了
566
+ if (options.format !== 'json') {
567
+ if (result.nonCompliantFiles > 0 && options.exitOnError) {
568
+ process.exit(1);
569
+ }
570
+ }
571
+ } catch (error) {
572
+ console.error('TSX 检查失败:', error);
573
+ process.exit(1);
574
+ }
575
+ });
576
+
577
+ // init-claude-hooks 命令
578
+ program
579
+ .command('init-claude-hooks')
580
+ .description('初始化 Claude Code TSX 检查器配置')
581
+ .option('-f, --force', '覆盖已存在的配置文件')
582
+ .option('-p, --project-only', '只初始化项目配置,不修改全局配置')
583
+ .option('-v, --verbose', '显示详细输出')
584
+ .option('-c, --cwd <cwd>', '工作目录', process.cwd())
585
+ .action(async function doInitClaudeHooks(command) {
586
+ const options = command.opts();
587
+
588
+ try {
589
+ const { initClaudeHooks } = require('../dist/index.js');
590
+
591
+ const result = await initClaudeHooks({
592
+ force: options.force,
593
+ projectOnly: options.projectOnly,
594
+ verbose: options.verbose,
595
+ cwd: options.cwd,
596
+ });
597
+
598
+ if (!result.success) {
599
+ console.error('❌ 初始化失败:', result.error);
600
+ process.exit(1);
601
+ }
602
+ } catch (error) {
603
+ console.error('❌ 初始化失败:', error.message);
604
+ process.exit(1);
605
+ }
606
+ });
607
+
608
+ // analyze-tsx-hooks 命令
609
+ program
610
+ .command('analyze-tsx-hooks')
611
+ .description('分析 TSX Hook 日志,排查误判问题')
612
+ .option('-r, --recent <number>', '显示最近的 N 条记录', '50')
613
+ .option('-b, --blocked-only', '只显示被阻止的文件')
614
+ .option('-p, --passed-only', '只显示通过的文件')
615
+ .option('-v, --verbose', '显示详细信息')
616
+ .option('-f, --file <filename>', '分析特定文件')
617
+ .option('-c, --cleanup', '清理旧日志')
618
+ .action(async function doAnalyzeTSXHooks(command) {
619
+ const options = command.opts();
620
+
621
+ try {
622
+ const { analyzeTSXHooks } = require('../dist/index.js');
623
+
624
+ await analyzeTSXHooks({
625
+ recent: parseInt(options.recent),
626
+ blockedOnly: options.blockedOnly,
627
+ passedOnly: options.passedOnly,
628
+ verbose: options.verbose,
629
+ file: options.file,
630
+ cleanup: options.cleanup,
631
+ });
632
+ } catch (error) {
633
+ console.error('❌ 分析失败:', error.message);
634
+ process.exit(1);
635
+ }
636
+ });
637
+
420
638
  program.parse(process.argv);
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.TSXComplianceChecker = void 0;
30
+ const parser_1 = require("@babel/parser");
31
+ const traverse_1 = __importDefault(require("@babel/traverse"));
32
+ const t = __importStar(require("@babel/types"));
33
+ class TSXComplianceChecker {
34
+ /**
35
+ * 使用 AST 检查 TSX 规范
36
+ */
37
+ static checkTSXCompliance(content, filename) {
38
+ const issues = [];
39
+ try {
40
+ // 使用 Babel 解析 TSX 代码
41
+ const ast = (0, parser_1.parse)(content, {
42
+ sourceType: 'module',
43
+ plugins: ['jsx', 'typescript', 'decorators-legacy', 'classProperties'],
44
+ errorRecovery: true,
45
+ });
46
+ // 遍历 AST 进行检查
47
+ (0, traverse_1.default)(ast, {
48
+ // 检查变量声明
49
+ VariableDeclarator(path) {
50
+ TSXComplianceChecker.checkVariableDeclaration(path, content, issues);
51
+ },
52
+ // 检查方法定义
53
+ ClassMethod(path) {
54
+ TSXComplianceChecker.checkMethodDefinition(path, content, issues);
55
+ },
56
+ // 检查 JSX 使用
57
+ JSXElement(path) {
58
+ TSXComplianceChecker.checkJSXUsage(path, content, issues);
59
+ },
60
+ // 检查类型注解
61
+ TSTypeAnnotation(path) {
62
+ TSXComplianceChecker.checkTypeAnnotation(path, content, issues);
63
+ },
64
+ });
65
+ // 检查必需的结构
66
+ this.checkRequiredStructures(ast, content, issues);
67
+ }
68
+ catch (error) {
69
+ issues.push({
70
+ type: 'error',
71
+ code: 'PARSE_ERROR',
72
+ message: `代码解析失败: ${error.message}`,
73
+ suggestion: '检查代码语法是否正确',
74
+ });
75
+ }
76
+ return {
77
+ isCompliant: issues.filter((issue) => issue.type === 'error').length === 0,
78
+ issues,
79
+ };
80
+ }
81
+ /**
82
+ * 检查变量声明
83
+ */
84
+ static checkVariableDeclaration(path, content, issues) {
85
+ const node = path.node;
86
+ // 检查是否在 render 方法中
87
+ const renderMethod = path.findParent((p) => p.isClassMethod() &&
88
+ t.isIdentifier(p.node.key) &&
89
+ p.node.key.name === 'render');
90
+ if (!renderMethod)
91
+ return;
92
+ // 检查是否在事件处理函数中
93
+ const isInEventHandler = this.isInEventHandler(path);
94
+ if (isInEventHandler)
95
+ return;
96
+ // 检查是否在非 JSX 返回的回调函数中
97
+ const isInNonJSXCallback = this.isInNonJSXCallback(path);
98
+ if (isInNonJSXCallback)
99
+ return;
100
+ // 检查是否在返回 JSX 的回调函数中(如 .map())
101
+ const isInJSXCallback = this.isInJSXCallback(path);
102
+ if (isInJSXCallback) {
103
+ // 这种情况需要报错
104
+ const { line, column } = this.getLocation(path, content);
105
+ const snippet = this.extractCodeSnippet(content, line);
106
+ issues.push({
107
+ type: 'error',
108
+ code: 'CALLBACK_VARIABLE_DECLARATION',
109
+ message: '返回 JSX 的回调函数中不能包含变量声明',
110
+ suggestion: '将变量声明移到回调函数外部,或使用内联表达式',
111
+ line,
112
+ column,
113
+ codeSnippet: snippet.snippet,
114
+ snippetStartLine: snippet.startLine,
115
+ snippetEndLine: snippet.endLine,
116
+ });
117
+ return;
118
+ }
119
+ // 其他在 render 方法中的变量声明都是错误的
120
+ const { line, column } = this.getLocation(path, content);
121
+ const snippet = this.extractCodeSnippet(content, line);
122
+ issues.push({
123
+ type: 'error',
124
+ code: 'RENDER_VARIABLE_DECLARATION',
125
+ message: 'render() 方法中不能包含变量声明',
126
+ suggestion: '移除所有 const/let/var 声明,直接使用 this.state.xxx',
127
+ line,
128
+ column,
129
+ codeSnippet: snippet.snippet,
130
+ snippetStartLine: snippet.startLine,
131
+ snippetEndLine: snippet.endLine,
132
+ });
133
+ }
134
+ /**
135
+ * 检查是否在事件处理函数中
136
+ */
137
+ static isInEventHandler(path) {
138
+ // 查找父级 JSX 属性
139
+ const jsxAttribute = path.findParent((p) => p.isJSXAttribute());
140
+ if (jsxAttribute && t.isJSXIdentifier(jsxAttribute.node.name)) {
141
+ const attrName = jsxAttribute.node.name.name;
142
+ // 检查是否是事件处理属性(onXxx)
143
+ if (/^on[A-Z]/.test(attrName)) {
144
+ return true;
145
+ }
146
+ }
147
+ // 检查是否在 addEventListener 回调中
148
+ const callExpression = path.findParent((p) => p.isCallExpression());
149
+ if (callExpression && t.isMemberExpression(callExpression.node.callee)) {
150
+ const memberExpr = callExpression.node.callee;
151
+ if (t.isIdentifier(memberExpr.property) &&
152
+ memberExpr.property.name === 'addEventListener') {
153
+ return true;
154
+ }
155
+ }
156
+ return false;
157
+ }
158
+ /**
159
+ * 检查是否在非 JSX 返回的回调函数中
160
+ */
161
+ static isInNonJSXCallback(path) {
162
+ // 查找父级函数
163
+ const parentFunction = path.findParent((p) => p.isArrowFunctionExpression() || p.isFunctionExpression());
164
+ if (!parentFunction)
165
+ return false;
166
+ // 检查函数是否返回 JSX
167
+ const returnsJSX = this.functionReturnsJSX(parentFunction);
168
+ // 如果不返回 JSX,则认为是非 JSX 回调
169
+ return !returnsJSX;
170
+ }
171
+ /**
172
+ * 检查是否在返回 JSX 的回调函数中
173
+ */
174
+ static isInJSXCallback(path) {
175
+ // 查找父级函数
176
+ const parentFunction = path.findParent((p) => p.isArrowFunctionExpression() || p.isFunctionExpression());
177
+ if (!parentFunction)
178
+ return false;
179
+ // 检查函数是否返回 JSX
180
+ return this.functionReturnsJSX(parentFunction);
181
+ }
182
+ /**
183
+ * 检查函数是否返回 JSX
184
+ */
185
+ static functionReturnsJSX(functionPath) {
186
+ let returnsJSX = false;
187
+ functionPath.traverse({
188
+ ReturnStatement(path) {
189
+ if (path.node.argument && t.isJSXElement(path.node.argument)) {
190
+ returnsJSX = true;
191
+ }
192
+ },
193
+ JSXElement(path) {
194
+ // 如果函数体直接包含 JSX(箭头函数的隐式返回)
195
+ if (path.parent === functionPath.node.body) {
196
+ returnsJSX = true;
197
+ }
198
+ },
199
+ });
200
+ return returnsJSX;
201
+ }
202
+ /**
203
+ * 检查方法定义
204
+ */
205
+ static checkMethodDefinition(path, content, issues) {
206
+ const node = path.node;
207
+ // 跳过 render 方法和渲染相关方法
208
+ if (t.isIdentifier(node.key)) {
209
+ const methodName = node.key.name;
210
+ const renderMethods = [
211
+ 'render',
212
+ 'renderItem',
213
+ 'renderContent',
214
+ 'renderHeader',
215
+ 'renderFooter',
216
+ ];
217
+ if (renderMethods.includes(methodName))
218
+ return;
219
+ // 检查方法中是否包含 JSX
220
+ let containsJSX = false;
221
+ path.traverse({
222
+ JSXElement() {
223
+ containsJSX = true;
224
+ },
225
+ });
226
+ if (containsJSX) {
227
+ const { line, column } = this.getLocation(path, content);
228
+ const snippet = this.extractCodeSnippet(content, line);
229
+ issues.push({
230
+ type: 'error',
231
+ code: 'METHOD_CONTAINS_JSX',
232
+ message: `方法 ${methodName} 不能包含 JSX 语法`,
233
+ suggestion: '只有 render() 方法可以包含 JSX,其他方法只处理逻辑',
234
+ line,
235
+ column,
236
+ codeSnippet: snippet.snippet,
237
+ snippetStartLine: snippet.startLine,
238
+ snippetEndLine: snippet.endLine,
239
+ });
240
+ }
241
+ }
242
+ }
243
+ /**
244
+ * 检查 JSX 使用
245
+ */
246
+ static checkJSXUsage(path, content, issues) {
247
+ // 暂时不实现,专注于变量声明检查
248
+ }
249
+ /**
250
+ * 检查类型注解
251
+ */
252
+ static checkTypeAnnotation(path, content, issues) {
253
+ // 暂时不实现,专注于变量声明检查
254
+ }
255
+ /**
256
+ * 检查必需的结构
257
+ */
258
+ static checkRequiredStructures(ast, content, issues) {
259
+ // 这里可以添加对必需结构的检查
260
+ // 如 IProps, IState, Document 类等
261
+ }
262
+ /**
263
+ * 获取节点在源码中的位置
264
+ */
265
+ static getLocation(path, content) {
266
+ const loc = path.node.loc;
267
+ return {
268
+ line: loc ? loc.start.line : 1,
269
+ column: loc ? loc.start.column : 0,
270
+ };
271
+ }
272
+ /**
273
+ * 提取代码片段
274
+ */
275
+ static extractCodeSnippet(content, lineNumber, contextLines = 3) {
276
+ const lines = content.split('\n');
277
+ const startLine = Math.max(1, lineNumber - contextLines);
278
+ const endLine = Math.min(lines.length, lineNumber + contextLines);
279
+ const snippetLines = [];
280
+ for (let i = startLine; i <= endLine; i++) {
281
+ const line = lines[i - 1] || '';
282
+ const prefix = i === lineNumber ? '>>> ' : ' ';
283
+ snippetLines.push(`${prefix}${i.toString().padStart(3)}: ${line}`);
284
+ }
285
+ return {
286
+ snippet: snippetLines.join('\n'),
287
+ startLine,
288
+ endLine,
289
+ };
290
+ }
291
+ /**
292
+ * 生成检查报告
293
+ */
294
+ static generateReport(result, filename) {
295
+ const { isCompliant, issues } = result;
296
+ let report = '=== TSX 规范检查报告 ===\n';
297
+ report += `文件: ${filename}\n`;
298
+ if (isCompliant) {
299
+ report += '✅ 检查通过:文件符合 TSX 规范\n';
300
+ }
301
+ else {
302
+ const errorCount = issues.filter((issue) => issue.type === 'error').length;
303
+ report += `❌ 检查失败:发现 ${errorCount} 个错误\n\n`;
304
+ report += '--- 问题详情 ---\n';
305
+ issues.forEach((issue, index) => {
306
+ const icon = issue.type === 'error' ? '❌' : '⚠️';
307
+ report += `${index + 1}. ${icon} [${issue.code}] ${issue.message}\n`;
308
+ if (issue.line) {
309
+ report += ` 📍 位置: 第 ${issue.line} 行\n`;
310
+ }
311
+ report += ` 💡 建议: ${issue.suggestion}\n`;
312
+ if (issue.codeSnippet) {
313
+ report += ` 📝 代码片段:\n${issue.codeSnippet}\n`;
314
+ }
315
+ report += '\n';
316
+ });
317
+ }
318
+ return report;
319
+ }
320
+ }
321
+ exports.TSXComplianceChecker = TSXComplianceChecker;