@devflow-tools/plugin-react 0.12.6 → 0.13.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.
Files changed (57) hide show
  1. package/dist/analyzers/__tests__/diagnose-bug.test.d.ts +2 -0
  2. package/dist/analyzers/__tests__/diagnose-bug.test.d.ts.map +1 -0
  3. package/dist/analyzers/__tests__/diagnose-bug.test.js +45 -0
  4. package/dist/analyzers/__tests__/diagnose-bug.test.js.map +1 -0
  5. package/dist/analyzers/__tests__/file-scanner.test.d.ts +2 -0
  6. package/dist/analyzers/__tests__/file-scanner.test.d.ts.map +1 -0
  7. package/dist/analyzers/__tests__/file-scanner.test.js +32 -0
  8. package/dist/analyzers/__tests__/file-scanner.test.js.map +1 -0
  9. package/dist/analyzers/__tests__/hooks-review.test.d.ts +2 -0
  10. package/dist/analyzers/__tests__/hooks-review.test.d.ts.map +1 -0
  11. package/dist/analyzers/__tests__/hooks-review.test.js +185 -0
  12. package/dist/analyzers/__tests__/hooks-review.test.js.map +1 -0
  13. package/dist/analyzers/__tests__/new-component.test.d.ts +2 -0
  14. package/dist/analyzers/__tests__/new-component.test.d.ts.map +1 -0
  15. package/dist/analyzers/__tests__/new-component.test.js +40 -0
  16. package/dist/analyzers/__tests__/new-component.test.js.map +1 -0
  17. package/dist/analyzers/__tests__/performance-audit.test.d.ts +2 -0
  18. package/dist/analyzers/__tests__/performance-audit.test.d.ts.map +1 -0
  19. package/dist/analyzers/__tests__/performance-audit.test.js +109 -0
  20. package/dist/analyzers/__tests__/performance-audit.test.js.map +1 -0
  21. package/dist/analyzers/__tests__/refactor-component.test.d.ts +2 -0
  22. package/dist/analyzers/__tests__/refactor-component.test.d.ts.map +1 -0
  23. package/dist/analyzers/__tests__/refactor-component.test.js +65 -0
  24. package/dist/analyzers/__tests__/refactor-component.test.js.map +1 -0
  25. package/dist/analyzers/diagnose-bug.d.ts +9 -0
  26. package/dist/analyzers/diagnose-bug.d.ts.map +1 -0
  27. package/dist/analyzers/diagnose-bug.js +108 -0
  28. package/dist/analyzers/diagnose-bug.js.map +1 -0
  29. package/dist/analyzers/file-scanner.d.ts +16 -0
  30. package/dist/analyzers/file-scanner.d.ts.map +1 -0
  31. package/dist/analyzers/file-scanner.js +143 -0
  32. package/dist/analyzers/file-scanner.js.map +1 -0
  33. package/dist/analyzers/hooks-review.d.ts +10 -0
  34. package/dist/analyzers/hooks-review.d.ts.map +1 -0
  35. package/dist/analyzers/hooks-review.js +307 -0
  36. package/dist/analyzers/hooks-review.js.map +1 -0
  37. package/dist/analyzers/new-component.d.ts +12 -0
  38. package/dist/analyzers/new-component.d.ts.map +1 -0
  39. package/dist/analyzers/new-component.js +59 -0
  40. package/dist/analyzers/new-component.js.map +1 -0
  41. package/dist/analyzers/performance-audit.d.ts +10 -0
  42. package/dist/analyzers/performance-audit.d.ts.map +1 -0
  43. package/dist/analyzers/performance-audit.js +239 -0
  44. package/dist/analyzers/performance-audit.js.map +1 -0
  45. package/dist/analyzers/refactor-component.d.ts +3 -0
  46. package/dist/analyzers/refactor-component.d.ts.map +1 -0
  47. package/dist/analyzers/refactor-component.js +90 -0
  48. package/dist/analyzers/refactor-component.js.map +1 -0
  49. package/dist/analyzers/types.d.ts +27 -0
  50. package/dist/analyzers/types.d.ts.map +1 -0
  51. package/dist/analyzers/types.js +2 -0
  52. package/dist/analyzers/types.js.map +1 -0
  53. package/dist/index.d.ts +3 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +1861 -0
  56. package/dist/index.js.map +1 -0
  57. package/package.json +3 -3
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diagnose-bug.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose-bug.test.d.ts","sourceRoot":"","sources":["../../../src/analyzers/__tests__/diagnose-bug.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { diagnoseBug } from '../diagnose-bug.js';
6
+ describe('diagnoseBug', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = mkdtempSync(join(tmpdir(), 'diagnose-'));
10
+ mkdirSync(join(tmpDir, 'src'));
11
+ });
12
+ afterEach(() => {
13
+ rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ it('analyzes error and returns root causes', () => {
16
+ writeFileSync(join(tmpDir, 'src', 'App.tsx'), `
17
+ import { useState } from 'react';
18
+ export function App() {
19
+ const [data, setData] = useState(null);
20
+ return <div>{data.name}</div>;
21
+ }
22
+ `);
23
+ const result = diagnoseBug({
24
+ projectRoot: tmpDir,
25
+ errorMessage: "Cannot read properties of null (reading 'name')",
26
+ stackTrace: 'at App (src/App.tsx:4)',
27
+ });
28
+ expect(result.success).toBe(true);
29
+ expect(result.data).toBeDefined();
30
+ expect(result.data.rootCauses).toBeDefined();
31
+ });
32
+ it('detects null access patterns', () => {
33
+ writeFileSync(join(tmpDir, 'src', 'User.tsx'), `
34
+ export function User({ user }: { user: any }) {
35
+ return <div>{user.profile.name}</div>;
36
+ }
37
+ `);
38
+ const result = diagnoseBug({
39
+ projectRoot: tmpDir,
40
+ errorMessage: "Cannot read properties of undefined (reading 'name')",
41
+ });
42
+ expect(result.data.rootCauses.length).toBeGreaterThan(0);
43
+ });
44
+ });
45
+ //# sourceMappingURL=diagnose-bug.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose-bug.test.js","sourceRoot":"","sources":["../../../src/analyzers/__tests__/diagnose-bug.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE;;;;;;CAMjD,CAAC,CAAC;QACC,MAAM,MAAM,GAAG,WAAW,CAAC;YACzB,WAAW,EAAE,MAAM;YACnB,YAAY,EAAE,iDAAiD;YAC/D,UAAU,EAAE,wBAAwB;SACrC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE;;;;CAIlD,CAAC,CAAC;QACC,MAAM,MAAM,GAAG,WAAW,CAAC;YACzB,WAAW,EAAE,MAAM;YACnB,YAAY,EAAE,sDAAsD;SACrE,CAAC,CAAC;QACH,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file-scanner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.test.d.ts","sourceRoot":"","sources":["../../../src/analyzers/__tests__/file-scanner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { scanFiles } from '../file-scanner.js';
6
+ describe('scanFiles', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = mkdtempSync(join(tmpdir(), 'analyzer-test-'));
10
+ mkdirSync(join(tmpDir, 'src'));
11
+ writeFileSync(join(tmpDir, 'src', 'App.tsx'), 'export function App() {}');
12
+ writeFileSync(join(tmpDir, 'src', 'utils.ts'), 'export const x = 1;');
13
+ mkdirSync(join(tmpDir, 'node_modules'));
14
+ writeFileSync(join(tmpDir, 'node_modules', 'lib.js'), '');
15
+ });
16
+ afterEach(() => {
17
+ rmSync(tmpDir, { recursive: true, force: true });
18
+ });
19
+ it('scans tsx/ts files excluding node_modules', () => {
20
+ const ctx = scanFiles(tmpDir);
21
+ expect(ctx.files).toHaveLength(2);
22
+ expect(ctx.files.some(f => f.endsWith('App.tsx'))).toBe(true);
23
+ expect(ctx.files.some(f => f.endsWith('utils.ts'))).toBe(true);
24
+ expect(ctx.files.some(f => f.includes('node_modules'))).toBe(false);
25
+ });
26
+ it('filters by extension', () => {
27
+ const ctx = scanFiles(tmpDir, { extensions: ['.tsx'] });
28
+ expect(ctx.files).toHaveLength(1);
29
+ expect(ctx.files[0]).toContain('App.tsx');
30
+ });
31
+ });
32
+ //# sourceMappingURL=file-scanner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.test.js","sourceRoot":"","sources":["../../../src/analyzers/__tests__/file-scanner.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAmB,MAAM,oBAAoB,CAAC;AAEhE,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;QACvD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/B,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,0BAA0B,CAAC,CAAC;QAC1E,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,qBAAqB,CAAC,CAAC;QACtE,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;QACxC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hooks-review.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-review.test.d.ts","sourceRoot":"","sources":["../../../src/analyzers/__tests__/hooks-review.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,185 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { reviewHooks } from '../hooks-review.js';
6
+ describe('reviewHooks', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = mkdtempSync(join(tmpdir(), 'hooks-review-'));
10
+ mkdirSync(join(tmpDir, 'src'));
11
+ });
12
+ afterEach(() => {
13
+ rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ it('detects conditional hook calls', () => {
16
+ writeFileSync(join(tmpDir, 'src', 'Bad.tsx'), `
17
+ import { useState } from 'react';
18
+ export function Bad({ flag }: { flag: boolean }) {
19
+ if (flag) {
20
+ const [x, setX] = useState(0);
21
+ }
22
+ return null;
23
+ }
24
+ `);
25
+ const result = reviewHooks({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'Bad.tsx')] });
26
+ expect(result.success).toBe(true);
27
+ expect(result.findings).toBeDefined();
28
+ const critical = result.findings.filter(f => f.severity === 'critical');
29
+ expect(critical.length).toBeGreaterThan(0);
30
+ expect(critical[0].category).toBe('conditional-hook');
31
+ });
32
+ it('detects hooks inside loops', () => {
33
+ writeFileSync(join(tmpDir, 'src', 'Loop.tsx'), `
34
+ import { useState } from 'react';
35
+ export function Loop({ items }: { items: string[] }) {
36
+ for (const item of items) {
37
+ const [x] = useState(item);
38
+ }
39
+ return null;
40
+ }
41
+ `);
42
+ const result = reviewHooks({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'Loop.tsx')] });
43
+ const critical = result.findings.filter(f => f.severity === 'critical');
44
+ expect(critical.length).toBeGreaterThan(0);
45
+ expect(critical[0].category).toBe('loop-hook');
46
+ });
47
+ it('detects missing useEffect cleanup', () => {
48
+ writeFileSync(join(tmpDir, 'src', 'Timer.tsx'), `
49
+ import { useEffect } from 'react';
50
+ export function Timer() {
51
+ useEffect(() => {
52
+ const id = setInterval(() => {}, 1000);
53
+ }, []);
54
+ return null;
55
+ }
56
+ `);
57
+ const result = reviewHooks({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'Timer.tsx')] });
58
+ const warnings = result.findings.filter(f => f.severity === 'warning');
59
+ expect(warnings.some(f => f.category === 'missing-cleanup')).toBe(true);
60
+ });
61
+ it('reports no critical issues for clean code', () => {
62
+ writeFileSync(join(tmpDir, 'src', 'Good.tsx'), `
63
+ import { useState, useEffect } from 'react';
64
+ export function Good() {
65
+ const [count, setCount] = useState(0);
66
+ useEffect(() => {
67
+ const timer = setTimeout(() => {}, 1000);
68
+ return () => clearTimeout(timer);
69
+ }, []);
70
+ return <div>{count}</div>;
71
+ }
72
+ `);
73
+ const result = reviewHooks({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'Good.tsx')] });
74
+ const critical = result.findings.filter(f => f.severity === 'critical');
75
+ expect(critical).toHaveLength(0);
76
+ });
77
+ it('returns metrics about hook usage', () => {
78
+ writeFileSync(join(tmpDir, 'src', 'App.tsx'), `
79
+ import { useState, useEffect, useMemo } from 'react';
80
+ export function App() {
81
+ const [a] = useState(1);
82
+ const [b] = useState(2);
83
+ useEffect(() => {}, []);
84
+ const c = useMemo(() => a + b, [a, b]);
85
+ return null;
86
+ }
87
+ `);
88
+ const result = reviewHooks({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'App.tsx')] });
89
+ expect(result.metrics).toBeDefined();
90
+ expect(result.metrics.useState).toBe(2);
91
+ expect(result.metrics.useEffect).toBe(1);
92
+ expect(result.metrics.useMemo).toBe(1);
93
+ });
94
+ it('supports target-scoped review and truncates findings by default', () => {
95
+ mkdirSync(join(tmpDir, 'src', 'components', 'live-player'), { recursive: true });
96
+ const targetFile = join(tmpDir, 'src', 'components', 'live-player', 'index.tsx');
97
+ writeFileSync(targetFile, `
98
+ import { useEffect, useState } from 'react';
99
+ export function LivePlayer({ flag, items }: { flag: boolean; items: string[] }) {
100
+ if (flag) {
101
+ const [count] = useState(0);
102
+ }
103
+ for (const item of items) {
104
+ const [value] = useState(item);
105
+ }
106
+ useEffect(() => {
107
+ const timer = setInterval(() => {}, 1000);
108
+ }, []);
109
+ return null;
110
+ }
111
+ `);
112
+ const result = reviewHooks({ projectRoot: tmpDir, files: [targetFile] }, { filePath: 'src/components/live-player/index.tsx', componentName: 'LivePlayer', scope: 'target', outputMode: 'summary', maxFindings: 2 });
113
+ expect(result.findings).toHaveLength(2);
114
+ expect(result.totalFindings).toBeGreaterThan(2);
115
+ expect(result.truncated).toBe(true);
116
+ expect(result.reportId).toContain('react-hooks-');
117
+ expect(result.summary).toContain('src/components/live-player/index.tsx');
118
+ });
119
+ it('does not flag top-level useEffect after conditional blocks', () => {
120
+ const file = join(tmpDir, 'src', 'LivePlayer.tsx');
121
+ writeFileSync(file, `
122
+ import { useEffect, useState } from 'react';
123
+ export function LivePlayer({ flag }: { flag: boolean }) {
124
+ const [count] = useState(0);
125
+ if (flag) {
126
+ console.log(count);
127
+ }
128
+ useEffect(() => {
129
+ window.addEventListener('resize', () => {});
130
+ return () => window.removeEventListener('resize', () => {});
131
+ }, []);
132
+ return null;
133
+ }
134
+ `);
135
+ const result = reviewHooks({ projectRoot: tmpDir, files: [file] }, { filePath: 'src/LivePlayer.tsx', componentName: 'LivePlayer', scope: 'target', outputMode: 'full' });
136
+ expect(result.findings.some((finding) => finding.category === 'conditional-hook')).toBe(false);
137
+ });
138
+ it('prefers exact file path over component usage matches in target scope', () => {
139
+ mkdirSync(join(tmpDir, 'src', 'pages'), { recursive: true });
140
+ const targetFile = join(tmpDir, 'src', 'LivePlayer.tsx');
141
+ const usageFile = join(tmpDir, 'src', 'pages', 'LiveRoomPage.tsx');
142
+ writeFileSync(targetFile, `
143
+ import { useEffect } from 'react';
144
+ export function LivePlayer() {
145
+ if (Math.random() > 2) {
146
+ useEffect(() => {}, []);
147
+ }
148
+ return null;
149
+ }
150
+ `);
151
+ writeFileSync(usageFile, `
152
+ import { LivePlayer } from '../LivePlayer';
153
+ export function LiveRoomPage() {
154
+ return <LivePlayer />;
155
+ }
156
+ `);
157
+ const result = reviewHooks({ projectRoot: tmpDir, files: [targetFile, usageFile] }, { filePath: 'src/LivePlayer.tsx', componentName: 'LivePlayer', scope: 'target', outputMode: 'full' });
158
+ expect(result.findings.every((finding) => finding.file === 'src/LivePlayer.tsx')).toBe(true);
159
+ });
160
+ it('resolves directory filePath and kebab-case componentName in target scope', () => {
161
+ mkdirSync(join(tmpDir, 'src', 'components', 'live-player'), { recursive: true });
162
+ mkdirSync(join(tmpDir, 'src', 'pages'), { recursive: true });
163
+ const targetFile = join(tmpDir, 'src', 'components', 'live-player', 'LivePlayer.tsx');
164
+ const usageFile = join(tmpDir, 'src', 'pages', 'LiveRoomPage.tsx');
165
+ writeFileSync(targetFile, `
166
+ import { useEffect } from 'react';
167
+ export const LivePlayer = () => {
168
+ if (Math.random() > 2) {
169
+ useEffect(() => {}, []);
170
+ }
171
+ return null;
172
+ }
173
+ `);
174
+ writeFileSync(usageFile, `
175
+ import { LivePlayer } from '../components/live-player/LivePlayer';
176
+ export function LiveRoomPage() {
177
+ return <LivePlayer />;
178
+ }
179
+ `);
180
+ const result = reviewHooks({ projectRoot: tmpDir, files: [targetFile, usageFile] }, { filePath: 'src/components/live-player', componentName: 'live-player', scope: 'target', outputMode: 'full' });
181
+ expect(result.findings.length).toBeGreaterThan(0);
182
+ expect(result.findings.every((finding) => finding.file === 'src/components/live-player/LivePlayer.tsx')).toBe(true);
183
+ });
184
+ });
185
+ //# sourceMappingURL=hooks-review.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-review.test.js","sourceRoot":"","sources":["../../../src/analyzers/__tests__/hooks-review.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=new-component.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-component.test.d.ts","sourceRoot":"","sources":["../../../src/analyzers/__tests__/new-component.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { generateComponent } from '../new-component.js';
6
+ describe('generateComponent', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = mkdtempSync(join(tmpdir(), 'new-comp-'));
10
+ mkdirSync(join(tmpDir, 'src', 'components'), { recursive: true });
11
+ writeFileSync(join(tmpDir, 'src', 'components', 'Existing.tsx'), `
12
+ import React from 'react';
13
+ export const Existing = React.memo(function Existing({ name }: { name: string }) {
14
+ return <div>{name}</div>;
15
+ });
16
+ `);
17
+ });
18
+ afterEach(() => {
19
+ rmSync(tmpDir, { recursive: true, force: true });
20
+ });
21
+ it('generates component code following project patterns', () => {
22
+ const result = generateComponent({
23
+ projectRoot: tmpDir,
24
+ componentName: 'UserCard',
25
+ props: [{ name: 'name', type: 'string' }, { name: 'avatar', type: 'string' }],
26
+ });
27
+ expect(result.success).toBe(true);
28
+ expect(result.data.generatedCode.component).toContain('UserCard');
29
+ expect(result.data.generatedCode.component).toContain('React.memo');
30
+ });
31
+ it('detects existing patterns', () => {
32
+ const result = generateComponent({
33
+ projectRoot: tmpDir,
34
+ componentName: 'Test',
35
+ props: [],
36
+ });
37
+ expect(result.data.analysis.existingPatterns.usesReactMemo).toBe(true);
38
+ });
39
+ });
40
+ //# sourceMappingURL=new-component.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-component.test.js","sourceRoot":"","sources":["../../../src/analyzers/__tests__/new-component.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE;;;;;CAKpE,CAAC,CAAC;IACD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,iBAAiB,CAAC;YAC/B,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,UAAU;YACzB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;SAC9E,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3E,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,iBAAiB,CAAC;YAC/B,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,MAAM;YACrB,KAAK,EAAE,EAAE;SACV,CAAC,CAAC;QACH,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,QAAQ,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=performance-audit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-audit.test.d.ts","sourceRoot":"","sources":["../../../src/analyzers/__tests__/performance-audit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { auditPerformance } from '../performance-audit.js';
6
+ describe('auditPerformance', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = mkdtempSync(join(tmpdir(), 'perf-audit-'));
10
+ mkdirSync(join(tmpDir, 'src'));
11
+ });
12
+ afterEach(() => {
13
+ rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ it('detects pure components without React.memo', () => {
16
+ writeFileSync(join(tmpDir, 'src', 'Card.tsx'), `
17
+ export function Card({ title, body }: { title: string; body: string }) {
18
+ return <div><h1>{title}</h1><p>{body}</p></div>;
19
+ }
20
+ `);
21
+ const result = auditPerformance({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'Card.tsx')] });
22
+ expect(result.findings.some(f => f.category === 'missing-memo')).toBe(true);
23
+ });
24
+ it('detects inline object creation in JSX', () => {
25
+ writeFileSync(join(tmpDir, 'src', 'List.tsx'), `
26
+ import { useState } from 'react';
27
+ export function List() {
28
+ const [items] = useState([]);
29
+ return items.map(item => <div key={item.id} style={{ color: 'red' }}>{item.name}</div>);
30
+ }
31
+ `);
32
+ const result = auditPerformance({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'List.tsx')] });
33
+ expect(result.findings.some(f => f.category === 'inline-object')).toBe(true);
34
+ });
35
+ it('returns metrics about components', () => {
36
+ writeFileSync(join(tmpDir, 'src', 'A.tsx'), 'export function A() { return null; }');
37
+ writeFileSync(join(tmpDir, 'src', 'B.tsx'), 'export function B() { return null; }');
38
+ const result = auditPerformance({ projectRoot: tmpDir, files: [join(tmpDir, 'src', 'A.tsx'), join(tmpDir, 'src', 'B.tsx')] });
39
+ expect(result.metrics.totalComponents).toBe(2);
40
+ });
41
+ it('supports target-scoped analysis and truncates findings by default', () => {
42
+ mkdirSync(join(tmpDir, 'src', 'components', 'live-player'), { recursive: true });
43
+ const targetFile = join(tmpDir, 'src', 'components', 'live-player', 'index.tsx');
44
+ writeFileSync(targetFile, `
45
+ export function LivePlayer() {
46
+ return (
47
+ <div style={{ color: 'red' }}>
48
+ <span style={{ fontSize: 12 }}>demo</span>
49
+ <button style={{ marginTop: 8 }}>play</button>
50
+ </div>
51
+ );
52
+ }
53
+ `);
54
+ const otherFile = join(tmpDir, 'src', 'Other.tsx');
55
+ writeFileSync(otherFile, `
56
+ export function Other() {
57
+ return <div style={{ color: 'blue' }}>other</div>;
58
+ }
59
+ `);
60
+ const result = auditPerformance({ projectRoot: tmpDir, files: [targetFile, otherFile] }, { filePath: 'src/components/live-player/index.tsx', componentName: 'LivePlayer', scope: 'target', outputMode: 'summary', maxFindings: 2 });
61
+ expect(result.findings).toHaveLength(2);
62
+ expect(result.totalFindings).toBeGreaterThan(2);
63
+ expect(result.truncated).toBe(true);
64
+ expect(result.reportId).toContain('react-perf-');
65
+ expect(result.summary).toContain('src/components/live-player/index.tsx');
66
+ expect(result.findings.every((finding) => finding.file === 'src/components/live-player/index.tsx')).toBe(true);
67
+ });
68
+ it('prefers exact file path over component usage matches in target scope', () => {
69
+ mkdirSync(join(tmpDir, 'src', 'components', 'live-player'), { recursive: true });
70
+ mkdirSync(join(tmpDir, 'src', 'pages'), { recursive: true });
71
+ const targetFile = join(tmpDir, 'src', 'components', 'live-player', 'index.tsx');
72
+ const usageFile = join(tmpDir, 'src', 'pages', 'LiveRoomPage.tsx');
73
+ writeFileSync(targetFile, `
74
+ export function LivePlayer() {
75
+ return <div style={{ color: 'red' }}>player</div>;
76
+ }
77
+ `);
78
+ writeFileSync(usageFile, `
79
+ import { LivePlayer } from '../components/live-player';
80
+ export function LiveRoomPage() {
81
+ return <section style={{ color: 'blue' }}><LivePlayer /></section>;
82
+ }
83
+ `);
84
+ const result = auditPerformance({ projectRoot: tmpDir, files: [targetFile, usageFile] }, { filePath: 'src/components/live-player/index.tsx', componentName: 'LivePlayer', scope: 'target', outputMode: 'full' });
85
+ expect(result.findings.every((finding) => finding.file === 'src/components/live-player/index.tsx')).toBe(true);
86
+ });
87
+ it('resolves directory filePath and kebab-case componentName to the target component file', () => {
88
+ mkdirSync(join(tmpDir, 'src', 'components', 'live-player'), { recursive: true });
89
+ mkdirSync(join(tmpDir, 'src', 'pages'), { recursive: true });
90
+ const targetFile = join(tmpDir, 'src', 'components', 'live-player', 'LivePlayer.tsx');
91
+ const usageFile = join(tmpDir, 'src', 'pages', 'LiveRoomPage.tsx');
92
+ writeFileSync(targetFile, `
93
+ export const LivePlayer = () => {
94
+ return <div style={{ color: 'red' }}>player</div>;
95
+ }
96
+ `);
97
+ writeFileSync(usageFile, `
98
+ import { LivePlayer } from '../components/live-player/LivePlayer';
99
+ export function LiveRoomPage() {
100
+ return <section style={{ color: 'blue' }}><LivePlayer /></section>;
101
+ }
102
+ `);
103
+ const result = auditPerformance({ projectRoot: tmpDir, files: [targetFile, usageFile] }, { filePath: 'src/components/live-player', componentName: 'live-player', scope: 'target', outputMode: 'full' });
104
+ expect(result.findings.length).toBeGreaterThan(0);
105
+ expect(result.findings.every((finding) => finding.file === 'src/components/live-player/LivePlayer.tsx')).toBe(true);
106
+ expect(result.metrics.totalComponents).toBe(1);
107
+ });
108
+ });
109
+ //# sourceMappingURL=performance-audit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-audit.test.js","sourceRoot":"","sources":["../../../src/analyzers/__tests__/performance-audit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=refactor-component.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refactor-component.test.d.ts","sourceRoot":"","sources":["../../../src/analyzers/__tests__/refactor-component.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { refactorComponent } from '../refactor-component.js';
6
+ describe('refactorComponent', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = mkdtempSync(join(tmpdir(), 'refactor-'));
10
+ mkdirSync(join(tmpDir, 'src'));
11
+ });
12
+ afterEach(() => {
13
+ rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ it('calculates complexity score', () => {
16
+ writeFileSync(join(tmpDir, 'src', 'Complex.tsx'), `
17
+ import { useState, useEffect, useMemo, useCallback } from 'react';
18
+ export function Complex({ a, b, c, d, e, f, g, h, i, j }: any) {
19
+ const [x, setX] = useState(0);
20
+ const [y, setY] = useState(0);
21
+ useEffect(() => {}, [x]);
22
+ const z = useMemo(() => x + y, [x, y]);
23
+ const handler = useCallback(() => setX(x + 1), [x]);
24
+ if (a) {
25
+ if (b) {
26
+ if (c) {
27
+ return <div>deep</div>;
28
+ }
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ `);
34
+ const result = refactorComponent(join(tmpDir, 'src', 'Complex.tsx'), tmpDir);
35
+ expect(result.success).toBe(true);
36
+ expect(result.data.complexity.score).toBeGreaterThan(50);
37
+ expect(result.data.complexity.lines).toBeGreaterThan(10);
38
+ });
39
+ it('identifies too many props', () => {
40
+ writeFileSync(join(tmpDir, 'src', 'ManyProps.tsx'), `
41
+ export function ManyProps({ a, b, c, d, e, f, g, h, i }: any) {
42
+ return <div />;
43
+ }
44
+ `);
45
+ const result = refactorComponent(join(tmpDir, 'src', 'ManyProps.tsx'), tmpDir);
46
+ expect(result.data.smells.some((s) => s.type === 'too-many-props')).toBe(true);
47
+ });
48
+ it('suggests extracting hooks for data fetching logic', () => {
49
+ writeFileSync(join(tmpDir, 'src', 'WithData.tsx'), `
50
+ import { useState, useEffect } from 'react';
51
+ export function WithData() {
52
+ const [data, setData] = useState(null);
53
+ const [loading, setLoading] = useState(true);
54
+ useEffect(() => {
55
+ fetch('/api/data').then(r => r.json()).then(d => { setData(d); setLoading(false); });
56
+ }, []);
57
+ if (loading) return <div>Loading...</div>;
58
+ return <div>{JSON.stringify(data)}</div>;
59
+ }
60
+ `);
61
+ const result = refactorComponent(join(tmpDir, 'src', 'WithData.tsx'), tmpDir);
62
+ expect(result.data.refactorPlan.some((p) => p.action === 'extract-hook')).toBe(true);
63
+ });
64
+ });
65
+ //# sourceMappingURL=refactor-component.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refactor-component.test.js","sourceRoot":"","sources":["../../../src/analyzers/__tests__/refactor-component.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,EAAE;;;;;;;;;;;;;;;;;CAiBrD,CAAC,CAAC;QACC,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7E,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAClE,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE;;;;CAIvD,CAAC,CAAC;QACC,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/E,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE;;;;;;;;;;;CAWtD,CAAC,CAAC;QACC,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9E,MAAM,CAAE,MAAM,CAAC,IAAY,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { AnalysisResult } from './types.js';
2
+ interface DiagnoseInput {
3
+ projectRoot: string;
4
+ errorMessage: string;
5
+ stackTrace?: string;
6
+ }
7
+ export declare function diagnoseBug(input: DiagnoseInput): AnalysisResult;
8
+ export {};
9
+ //# sourceMappingURL=diagnose-bug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose-bug.d.ts","sourceRoot":"","sources":["../../src/analyzers/diagnose-bug.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,YAAY,CAAC;AAKlE,UAAU,aAAa;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA2FhE"}