1- #!/usr/bin/env node
21/**
32 * @fileoverview Validates that no individual files exceed size threshold.
43 *
87 * - Excludes: node_modules, .git, dist, build, coverage directories
98 */
109
11- import { promises as fs } from 'node:fs' ;
12- import path from 'node:path' ;
13- import { fileURLToPath } from 'node:url' ;
14- import { logger } from './utils/logger.mjs' ;
10+ import { promises as fs } from 'node:fs'
11+ import path from 'node:path'
12+ import { fileURLToPath } from 'node:url'
13+ import { logger } from './utils/logger.mjs'
1514
16- const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
17- const rootPath = path . join ( __dirname , '..' ) ;
15+ const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) )
16+ const rootPath = path . join ( __dirname , '..' )
1817
1918// Maximum file size: 2MB
20- const MAX_FILE_SIZE = 2 * 1024 * 1024 ; // 2,097,152 bytes
19+ const MAX_FILE_SIZE = 2 * 1024 * 1024 // 2,097,152 bytes
2120
2221// Directories to skip
2322const SKIP_DIRS = new Set ( [
@@ -34,28 +33,30 @@ const SKIP_DIRS = new Set([
3433 '.vercel' ,
3534 '.vscode' ,
3635 'tmp' ,
37- ] ) ;
36+ ] )
3837
3938/**
4039 * Format bytes to human-readable size.
4140 */
4241function formatBytes ( bytes ) {
43- if ( bytes === 0 ) return '0 B' ;
44- const k = 1024 ;
45- const sizes = [ 'B' , 'KB' , 'MB' , 'GB' ] ;
46- const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
47- return `${ ( bytes / Math . pow ( k , i ) ) . toFixed ( 2 ) } ${ sizes [ i ] } ` ;
42+ if ( bytes === 0 ) {
43+ return '0 B'
44+ }
45+ const k = 1024
46+ const sizes = [ 'B' , 'KB' , 'MB' , 'GB' ]
47+ const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) )
48+ return `${ ( bytes / k ** i ) . toFixed ( 2 ) } ${ sizes [ i ] } `
4849}
4950
5051/**
5152 * Recursively scan directory for files exceeding size limit.
5253 */
5354async function scanDirectory ( dir , violations = [ ] ) {
5455 try {
55- const entries = await fs . readdir ( dir , { withFileTypes : true } ) ;
56+ const entries = await fs . readdir ( dir , { withFileTypes : true } )
5657
5758 for ( const entry of entries ) {
58- const fullPath = path . join ( dir , entry . name ) ;
59+ const fullPath = path . join ( dir , entry . name )
5960
6061 if ( entry . isDirectory ( ) ) {
6162 // Skip excluded directories and hidden directories (except .claude, .config, .github)
@@ -66,19 +67,19 @@ async function scanDirectory(dir, violations = []) {
6667 entry . name === '.config' ||
6768 entry . name === '.github' )
6869 ) {
69- await scanDirectory ( fullPath , violations ) ;
70+ await scanDirectory ( fullPath , violations )
7071 }
7172 } else if ( entry . isFile ( ) ) {
7273 try {
73- const stats = await fs . stat ( fullPath ) ;
74+ const stats = await fs . stat ( fullPath )
7475 if ( stats . size > MAX_FILE_SIZE ) {
75- const relativePath = path . relative ( rootPath , fullPath ) ;
76+ const relativePath = path . relative ( rootPath , fullPath )
7677 violations . push ( {
7778 file : relativePath ,
7879 size : stats . size ,
7980 formattedSize : formatBytes ( stats . size ) ,
8081 maxSize : formatBytes ( MAX_FILE_SIZE ) ,
81- } ) ;
82+ } )
8283 }
8384 } catch {
8485 // Skip files we can't stat
@@ -89,58 +90,60 @@ async function scanDirectory(dir, violations = []) {
8990 // Skip directories we can't read
9091 }
9192
92- return violations ;
93+ return violations
9394}
9495
9596/**
9697 * Validate file sizes in repository.
9798 */
9899async function validateFileSizes ( ) {
99- const violations = await scanDirectory ( rootPath ) ;
100+ const violations = await scanDirectory ( rootPath )
100101
101102 // Sort by size descending (largest first)
102- violations . sort ( ( a , b ) => b . size - a . size ) ;
103+ violations . sort ( ( a , b ) => b . size - a . size )
103104
104- return violations ;
105+ return violations
105106}
106107
107108async function main ( ) {
108109 try {
109- const violations = await validateFileSizes ( ) ;
110+ const violations = await validateFileSizes ( )
110111
111112 if ( violations . length === 0 ) {
112- logger . success ( 'All files are within size limits' ) ;
113- process . exitCode = 0 ;
114- return ;
113+ logger . success ( 'All files are within size limits' )
114+ process . exitCode = 0
115+ return
115116 }
116117
117- logger . fail ( 'File size violations found' ) ;
118- logger . log ( '' ) ;
119- logger . log ( `Maximum allowed file size: ${ formatBytes ( MAX_FILE_SIZE ) } ` ) ;
120- logger . log ( '' ) ;
121- logger . log ( 'Files exceeding limit:' ) ;
122- logger . log ( '' ) ;
118+ logger . fail ( 'File size violations found' )
119+ logger . log ( '' )
120+ logger . log ( `Maximum allowed file size: ${ formatBytes ( MAX_FILE_SIZE ) } ` )
121+ logger . log ( '' )
122+ logger . log ( 'Files exceeding limit:' )
123+ logger . log ( '' )
123124
124125 for ( const violation of violations ) {
125- logger . log ( ` ${ violation . file } ` ) ;
126- logger . log ( ` Size: ${ violation . formattedSize } ` ) ;
127- logger . log ( ` Exceeds limit by: ${ formatBytes ( violation . size - MAX_FILE_SIZE ) } ` ) ;
128- logger . log ( '' ) ;
126+ logger . log ( ` ${ violation . file } ` )
127+ logger . log ( ` Size: ${ violation . formattedSize } ` )
128+ logger . log (
129+ ` Exceeds limit by: ${ formatBytes ( violation . size - MAX_FILE_SIZE ) } ` ,
130+ )
131+ logger . log ( '' )
129132 }
130133
131134 logger . log (
132135 'Reduce file sizes, move large files to external storage, or exclude from repository.' ,
133- ) ;
134- logger . log ( '' ) ;
136+ )
137+ logger . log ( '' )
135138
136- process . exitCode = 1 ;
139+ process . exitCode = 1
137140 } catch ( error ) {
138- logger . fail ( `Validation failed: ${ error . message } ` ) ;
139- process . exitCode = 1 ;
141+ logger . fail ( `Validation failed: ${ error . message } ` )
142+ process . exitCode = 1
140143 }
141144}
142145
143146main ( ) . catch ( error => {
144- logger . fail ( `Validation failed: ${ error } ` ) ;
145- process . exitCode = 1 ;
146- } ) ;
147+ logger . fail ( `Validation failed: ${ error } ` )
148+ process . exitCode = 1
149+ } )
0 commit comments