From 4fa456c98313af7d5628a8774f32f806fe549973 Mon Sep 17 00:00:00 2001 From: Tuan Date: Sat, 11 Jan 2025 09:08:56 +0700 Subject: [PATCH 1/2] fix: resolve TypeScript and linting issues - Remove unused imports - Replace any types with proper type definitions - Add proper interfaces for code splitting and tree shaking - Fix error handling types --- src/__tests__/history.test.ts | 4 ++-- src/ai/analyzer.ts | 34 +++++++++++++++++++++++-------- src/plugins/history/index.ts | 38 +++++++++++++++++------------------ src/types/history.ts | 2 +- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/__tests__/history.test.ts b/src/__tests__/history.test.ts index 8955d1e..019c42e 100644 --- a/src/__tests__/history.test.ts +++ b/src/__tests__/history.test.ts @@ -1,5 +1,4 @@ import { BundleSizeHistory } from '../plugins/history'; -import { BundleStats } from '../types/history'; import { mkdir, writeFile, rm } from 'fs/promises'; import { join } from 'path'; import { tmpdir } from 'os'; @@ -264,7 +263,8 @@ describe('BundleSizeHistory', () => { }); it('should handle invalid import data', async () => { - const result = await history.importHistory({} as any); + const invalidData = {} as Partial; + const result = await history.importHistory(invalidData as ExportData); expect(result.success).toBe(false); expect(result.message).toContain('Invalid export data format'); }); diff --git a/src/ai/analyzer.ts b/src/ai/analyzer.ts index 65ad7f7..0d1c0c7 100644 --- a/src/ai/analyzer.ts +++ b/src/ai/analyzer.ts @@ -7,6 +7,27 @@ export interface AIAnalyzerOptions { temperature?: number; } +interface CodeSplittingResult { + splitPoints: string[]; + impact: Array<{ + path: string; + sizeReduction: number; + }>; +} + +interface TreeShakingResult { + unusedExports: Array<{ + module: string; + exports: string[]; + }>; + potentialSavings: number; +} + +interface ImpactEstimation { + sizeReduction: number; + performanceImprovement: number; +} + export class AIBundleAnalyzer { private openai: OpenAI; private model: string; @@ -98,7 +119,7 @@ export class AIBundleAnalyzer { .map(line => line.trim()); } - private parseCodeSplitting(suggestions: string): any { + private parseCodeSplitting(suggestions: string): CodeSplittingResult { const lines = suggestions.split('\n').filter(line => line.trim()); const splitPoints = lines .filter(line => line.includes('/')) @@ -116,7 +137,7 @@ export class AIBundleAnalyzer { }; } - private parseTreeShaking(suggestions: string): any { + private parseTreeShaking(suggestions: string): TreeShakingResult { const lines = suggestions.split('\n').filter(line => line.trim()); const unusedExports = lines .filter(line => line.includes('export')) @@ -124,20 +145,17 @@ export class AIBundleAnalyzer { const [module, ...exports] = line.split(':').map(s => s.trim()); return { module, - exports: exports[0].split(',').map(e => e.trim()) + exports: exports.join(':').split(',').map(e => e.trim()) }; }); return { unusedExports, - potentialSavings: unusedExports.length * 1000 // Example estimation + potentialSavings: unusedExports.length * 1024 // Example estimation }; } - private estimateImpact(suggestions: string, bundleInfo: BundleInfo): { - sizeReduction: number; - performanceImprovement: number; - } { + private estimateImpact(suggestions: string, bundleInfo: BundleInfo): ImpactEstimation { const totalSize = bundleInfo.size.raw; const codeSplitting = this.parseCodeSplitting(suggestions); const treeShaking = this.parseTreeShaking(suggestions); diff --git a/src/plugins/history/index.ts b/src/plugins/history/index.ts index 8da971b..4b3c20e 100644 --- a/src/plugins/history/index.ts +++ b/src/plugins/history/index.ts @@ -43,7 +43,7 @@ export class BundleSizeHistory { async initialize(): Promise { try { await mkdir(this.historyPath, { recursive: true }); - } catch (error: any) { + } catch (error: Error) { throw new Error(`Failed to create history directory: ${error.message}`); } } @@ -53,7 +53,7 @@ export class BundleSizeHistory { const historyFile = join(this.historyPath, 'history.json'); try { - let history = []; + let history: Array = []; try { const data = await readFile(historyFile, 'utf8'); history = JSON.parse(data); @@ -78,7 +78,7 @@ export class BundleSizeHistory { // Save alerts await this.saveAlerts(); - } catch (error: any) { + } catch (error: Error) { throw new Error(`Failed to save history: ${error.message}`); } } @@ -87,7 +87,7 @@ export class BundleSizeHistory { const alertsFile = join(this.historyPath, 'alerts.json'); try { await writeFile(alertsFile, JSON.stringify(this.alerts, null, 2)); - } catch (error: any) { + } catch (error: Error) { throw new Error(`Failed to save alerts: ${error.message}`); } } @@ -193,7 +193,7 @@ export class BundleSizeHistory { alerts, thresholds: this.thresholds }; - } catch (error: any) { + } catch (error: Error) { throw new Error(`Failed to export history: ${error.message}`); } } @@ -232,7 +232,7 @@ export class BundleSizeHistory { entriesImported: data.history.length, alerts: this.alerts }; - } catch (error: any) { + } catch (error: Error) { return { success: false, message: `Failed to import history: ${error.message}`, @@ -247,7 +247,7 @@ export class BundleSizeHistory { try { const data = await readFile(historyFile, 'utf8'); - const history = JSON.parse(data); + const history: Array = JSON.parse(data); if (history.length < 2) { return { @@ -275,7 +275,7 @@ export class BundleSizeHistory { averageSize, recommendations }; - } catch (error: any) { + } catch (error: Error) { throw new Error(`Failed to analyze trends: ${error.message}`); } } @@ -285,9 +285,9 @@ export class BundleSizeHistory { try { const data = await readFile(historyFile, 'utf8'); - const history = JSON.parse(data); + const history: Array = JSON.parse(data); return await this.visualizer.generateReport(history); - } catch (error: any) { + } catch (error: Error) { throw new Error(`Failed to generate report: ${error.message}`); } } @@ -297,7 +297,7 @@ export class BundleSizeHistory { try { const data = await readFile(historyFile, 'utf8'); - let history: (BundleStats & { timestamp: string })[] = JSON.parse(data); + let history: Array = JSON.parse(data); // Apply date filters if (query.startDate) { @@ -397,7 +397,7 @@ export class BundleSizeHistory { }, summary }; - } catch (error: any) { + } catch (error: Error) { if (error.code === 'ENOENT') { return { entries: [], @@ -424,7 +424,7 @@ export class BundleSizeHistory { try { const data = await readFile(historyFile, 'utf8'); - const history: BundleStats[] = JSON.parse(data); + const history: Array = JSON.parse(data); const chunkNames = new Set(); history.forEach(entry => { @@ -434,7 +434,7 @@ export class BundleSizeHistory { }); return Array.from(chunkNames).sort(); - } catch (error: any) { + } catch (error: Error) { if (error.code === 'ENOENT') { return []; } @@ -447,7 +447,7 @@ export class BundleSizeHistory { try { const data = await readFile(historyFile, 'utf8'); - const history: (BundleStats & { timestamp: string })[] = JSON.parse(data); + const history: Array = JSON.parse(data); if (history.length === 0) { return { earliest: null, latest: null }; @@ -458,7 +458,7 @@ export class BundleSizeHistory { earliest: new Date(Math.min(...timestamps)), latest: new Date(Math.max(...timestamps)) }; - } catch (error: any) { + } catch (error: Error) { if (error.code === 'ENOENT') { return { earliest: null, latest: null }; } @@ -471,7 +471,7 @@ export class BundleSizeHistory { try { const data = await readFile(historyFile, 'utf8'); - const history: BundleStats[] = JSON.parse(data); + const history: Array = JSON.parse(data); if (history.length === 0) { return { min: 0, max: 0, average: 0 }; @@ -483,7 +483,7 @@ export class BundleSizeHistory { max: Math.max(...sizes), average: sizes.reduce((a, b) => a + b, 0) / sizes.length }; - } catch (error: any) { + } catch (error: Error) { if (error.code === 'ENOENT') { return { min: 0, max: 0, average: 0 }; } @@ -500,7 +500,7 @@ export class BundleSizeHistory { private generateRecommendations( trend: string, percentageChange: number, - history: BundleStats[] + history: Array ): string[] { const recommendations: string[] = []; diff --git a/src/types/history.ts b/src/types/history.ts index f43281e..7bf40d8 100644 --- a/src/types/history.ts +++ b/src/types/history.ts @@ -10,7 +10,7 @@ export interface BundleStats { size: number; }[]; }[]; - [key: string]: any; + [key: string]: number | string | BundleStats['chunks']; } export interface HistoryOptions { From b6ae17e478e1f6043e6cea492a96315d4785e43e Mon Sep 17 00:00:00 2001 From: Tuan Date: Sat, 11 Jan 2025 09:17:49 +0700 Subject: [PATCH 2/2] fix: resolve TypeScript build errors - Add proper error type handling with unknown type - Fix ENOENT error checks - Add missing ExportData import - Improve type safety in queryHistory method - Fix undefined checks for query parameters --- src/__tests__/history.test.ts | 1 + src/plugins/history/index.ts | 198 +++++++++++++++++++--------------- 2 files changed, 110 insertions(+), 89 deletions(-) diff --git a/src/__tests__/history.test.ts b/src/__tests__/history.test.ts index 019c42e..93730c7 100644 --- a/src/__tests__/history.test.ts +++ b/src/__tests__/history.test.ts @@ -2,6 +2,7 @@ import { BundleSizeHistory } from '../plugins/history'; import { mkdir, writeFile, rm } from 'fs/promises'; import { join } from 'path'; import { tmpdir } from 'os'; +import { ExportData } from '../types/history'; describe('BundleSizeHistory', () => { let history: BundleSizeHistory; diff --git a/src/plugins/history/index.ts b/src/plugins/history/index.ts index 4b3c20e..b3e5f94 100644 --- a/src/plugins/history/index.ts +++ b/src/plugins/history/index.ts @@ -43,8 +43,11 @@ export class BundleSizeHistory { async initialize(): Promise { try { await mkdir(this.historyPath, { recursive: true }); - } catch (error: Error) { - throw new Error(`Failed to create history directory: ${error.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(`Failed to create history directory: ${error.message}`); + } + throw error; } } @@ -78,8 +81,11 @@ export class BundleSizeHistory { // Save alerts await this.saveAlerts(); - } catch (error: Error) { - throw new Error(`Failed to save history: ${error.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(`Failed to save history: ${error.message}`); + } + throw error; } } @@ -87,8 +93,11 @@ export class BundleSizeHistory { const alertsFile = join(this.historyPath, 'alerts.json'); try { await writeFile(alertsFile, JSON.stringify(this.alerts, null, 2)); - } catch (error: Error) { - throw new Error(`Failed to save alerts: ${error.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(`Failed to save alerts: ${error.message}`); + } + throw error; } } @@ -193,8 +202,11 @@ export class BundleSizeHistory { alerts, thresholds: this.thresholds }; - } catch (error: Error) { - throw new Error(`Failed to export history: ${error.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(`Failed to export history: ${error.message}`); + } + throw error; } } @@ -232,13 +244,16 @@ export class BundleSizeHistory { entriesImported: data.history.length, alerts: this.alerts }; - } catch (error: Error) { - return { - success: false, - message: `Failed to import history: ${error.message}`, - entriesImported: 0, - alerts: [] - }; + } catch (error: unknown) { + if (error instanceof Error) { + return { + success: false, + message: `Failed to import history: ${error.message}`, + entriesImported: 0, + alerts: [] + }; + } + throw error; } } @@ -275,8 +290,11 @@ export class BundleSizeHistory { averageSize, recommendations }; - } catch (error: Error) { - throw new Error(`Failed to analyze trends: ${error.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(`Failed to analyze trends: ${error.message}`); + } + throw error; } } @@ -287,8 +305,11 @@ export class BundleSizeHistory { const data = await readFile(historyFile, 'utf8'); const history: Array = JSON.parse(data); return await this.visualizer.generateReport(history); - } catch (error: Error) { - throw new Error(`Failed to generate report: ${error.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(`Failed to generate report: ${error.message}`); + } + throw error; } } @@ -301,90 +322,77 @@ export class BundleSizeHistory { // Apply date filters if (query.startDate) { - history = history.filter(entry => - new Date(entry.timestamp) >= query.startDate! - ); + history = history.filter(entry => new Date(entry.timestamp) >= query.startDate!); } if (query.endDate) { - history = history.filter(entry => - new Date(entry.timestamp) <= query.endDate! - ); + history = history.filter(entry => new Date(entry.timestamp) <= query.endDate!); } // Apply size filters - if (query.minSize) { + if (query.minSize !== undefined) { history = history.filter(entry => entry.totalSize >= query.minSize!); } - if (query.maxSize) { + if (query.maxSize !== undefined) { history = history.filter(entry => entry.totalSize <= query.maxSize!); } // Apply chunk filters if (query.chunkNames && query.chunkNames.length > 0) { history = history.filter(entry => - entry.chunks.some(chunk => - query.chunkNames!.includes(chunk.name) - ) + entry.chunks.some(chunk => query.chunkNames!.includes(chunk.name)) ); } // Calculate summary const summary = { - averageSize: history.length > 0 - ? history.reduce((sum, entry) => sum + entry.totalSize, 0) / history.length - : 0, - minSize: history.length > 0 - ? Math.min(...history.map(entry => entry.totalSize)) - : 0, - maxSize: history.length > 0 - ? Math.max(...history.map(entry => entry.totalSize)) - : 0, + averageSize: 0, + minSize: 0, + maxSize: 0, totalEntries: history.length }; - // Apply sorting - const sortBy = query.sortBy || 'date'; - const sortOrder = query.sortOrder || 'desc'; - - history.sort((a, b) => { - let valueA: number | Date, valueB: number | Date; - - switch (sortBy) { - case 'date': - valueA = new Date(a.timestamp); - valueB = new Date(b.timestamp); - break; - case 'totalSize': - valueA = a.totalSize; - valueB = b.totalSize; - break; - case 'gzipSize': - valueA = a.gzipSize; - valueB = b.gzipSize; - break; - case 'brotliSize': - valueA = a.brotliSize; - valueB = b.brotliSize; - break; - default: - valueA = new Date(a.timestamp); - valueB = new Date(b.timestamp); - } + if (history.length > 0) { + const sizes = history.map(entry => entry.totalSize); + summary.averageSize = sizes.reduce((a, b) => a + b, 0) / sizes.length; + summary.minSize = Math.min(...sizes); + summary.maxSize = Math.max(...sizes); + } - if (valueA instanceof Date && valueB instanceof Date) { - return sortOrder === 'asc' - ? valueA.getTime() - valueB.getTime() - : valueB.getTime() - valueA.getTime(); - } + // Apply sorting + if (query.sortBy) { + history.sort((a, b) => { + let valueA: number; + let valueB: number; + + switch (query.sortBy) { + case 'date': + valueA = new Date(a.timestamp).getTime(); + valueB = new Date(b.timestamp).getTime(); + break; + case 'totalSize': + valueA = a.totalSize; + valueB = b.totalSize; + break; + case 'gzipSize': + valueA = a.gzipSize; + valueB = b.gzipSize; + break; + case 'brotliSize': + valueA = a.brotliSize; + valueB = b.brotliSize; + break; + default: + valueA = a.totalSize; + valueB = b.totalSize; + } - return sortOrder === 'asc' - ? (valueA as number) - (valueB as number) - : (valueB as number) - (valueA as number); - }); + return query.sortOrder === 'desc' ? valueB - valueA : valueA - valueB; + }); + } // Apply pagination - const limit = query.limit || 10; const offset = query.offset || 0; + const limit = query.limit || 10; const paginatedHistory = history.slice(offset, offset + limit); return { @@ -397,8 +405,8 @@ export class BundleSizeHistory { }, summary }; - } catch (error: Error) { - if (error.code === 'ENOENT') { + } catch (error: unknown) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { return { entries: [], total: 0, @@ -415,7 +423,10 @@ export class BundleSizeHistory { } }; } - throw new Error(`Failed to query history: ${error.message}`); + if (error instanceof Error) { + throw new Error(`Failed to query history: ${error.message}`); + } + throw error; } } @@ -434,11 +445,14 @@ export class BundleSizeHistory { }); return Array.from(chunkNames).sort(); - } catch (error: Error) { - if (error.code === 'ENOENT') { + } catch (error: unknown) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { return []; } - throw new Error(`Failed to get chunk names: ${error.message}`); + if (error instanceof Error) { + throw new Error(`Failed to get chunk names: ${error.message}`); + } + throw error; } } @@ -458,11 +472,14 @@ export class BundleSizeHistory { earliest: new Date(Math.min(...timestamps)), latest: new Date(Math.max(...timestamps)) }; - } catch (error: Error) { - if (error.code === 'ENOENT') { + } catch (error: unknown) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { return { earliest: null, latest: null }; } - throw new Error(`Failed to get date range: ${error.message}`); + if (error instanceof Error) { + throw new Error(`Failed to get date range: ${error.message}`); + } + throw error; } } @@ -483,11 +500,14 @@ export class BundleSizeHistory { max: Math.max(...sizes), average: sizes.reduce((a, b) => a + b, 0) / sizes.length }; - } catch (error: Error) { - if (error.code === 'ENOENT') { + } catch (error: unknown) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { return { min: 0, max: 0, average: 0 }; } - throw new Error(`Failed to get size range: ${error.message}`); + if (error instanceof Error) { + throw new Error(`Failed to get size range: ${error.message}`); + } + throw error; } }