11import process from "node:process" ;
22import { promises as fs } from "node:fs" ;
3+ import { Command } from "commander" ;
34
4- /**
5- * Calculates the lines, words, and bytes for a given file.
6- */
7- async function calculateFileStats ( filePath ) {
8- const fileBuffer = await fs . readFile ( filePath ) ;
9- const fileContent = fileBuffer . toString ( ) ;
10-
11- // Standard 'wc' counts newline characters (\n)
12- const lineCount = fileContent . split ( "\n" ) . length - 1 ;
5+ const program = new Command ( ) ;
136
14- // Split by any whitespace and filter out empty strings to get actual words
15- const wordCount = fileContent
16- . split ( / \s + / )
17- . filter ( ( word ) => word . length > 0 ) . length ;
18-
19- // Buffer.length provides the exact byte count on disk
20- const byteCount = fileBuffer . length ;
21-
22- return { lineCount, wordCount, byteCount, filePath } ;
23- }
7+ program
8+ . name ( "wc" )
9+ . description ( "A simple node implementation of the word count utility" )
10+ . argument ( "[files...]" , "Files to process" )
11+ . option ( "-l, --lines" , "print the newline counts" )
12+ . option ( "-w, --words" , "print the word counts" )
13+ . option ( "-c, --bytes" , "print the byte counts" )
14+ . action ( async ( files , options ) => {
15+ // If no specific flags are provided, default to showing all
16+ const noFlagsProvided = ! options . lines && ! options . words && ! options . bytes ;
17+ const showAll = noFlagsProvided ;
2418
25- /**
26- * Main execution function
27- */
28- async function runWordCount ( ) {
29- const filePaths = process . argv . slice ( 2 ) ;
30- const results = [ ] ;
19+ const results = [ ] ;
3120
32- for ( const filePath of filePaths ) {
33- try {
34- const stats = await calculateFileStats ( filePath ) ;
35- results . push ( stats ) ;
21+ for ( const filePath of files ) {
22+ try {
23+ const stats = await calculateFileStats ( filePath ) ;
24+ results . push ( stats ) ;
25+ printReport ( stats , options , showAll ) ;
26+ } catch ( error ) {
27+ console . error ( `wc: ${ filePath } : No such file or directory` ) ;
28+ process . exitCode = 1 ;
29+ }
30+ }
3631
37- printFormattedLine (
38- stats . lineCount ,
39- stats . wordCount ,
40- stats . byteCount ,
41- stats . filePath ,
42- ) ;
43- } catch ( error ) {
44- console . error ( `wc: ${ filePath } : No such file or directory` ) ;
45- process . exitCode = 1 ;
32+ if ( results . length > 1 ) {
33+ const totals = {
34+ lineCount : results . reduce ( ( sum , s ) => sum + s . lineCount , 0 ) ,
35+ wordCount : results . reduce ( ( sum , s ) => sum + s . wordCount , 0 ) ,
36+ byteCount : results . reduce ( ( sum , s ) => sum + s . byteCount , 0 ) ,
37+ label : "total"
38+ } ;
39+ printReport ( totals , options , showAll ) ;
4640 }
47- }
41+ } ) ;
4842
49- // If multiple files were processed, show the total
50- if ( results . length > 1 ) {
51- const totalLines = results . reduce ( ( sum , item ) => sum + item . lineCount , 0 ) ;
52- const totalWords = results . reduce ( ( sum , item ) => sum + item . wordCount , 0 ) ;
53- const totalBytes = results . reduce ( ( sum , item ) => sum + item . byteCount , 0 ) ;
43+ async function calculateFileStats ( filePath ) {
44+ const fileBuffer = await fs . readFile ( filePath ) ;
45+ const fileContent = fileBuffer . toString ( ) ;
5446
55- printFormattedLine ( totalLines , totalWords , totalBytes , "total" ) ;
56- }
47+ return {
48+ lineCount : fileContent . split ( "\n" ) . length - 1 ,
49+ wordCount : fileContent . split ( / \s + / ) . filter ( w => w . length > 0 ) . length ,
50+ byteCount : fileBuffer . length ,
51+ label : filePath
52+ } ;
5753}
5854
59- /**
60- * Formats the output into columns to match the standard 'wc' utility
61- */
62- function printFormattedLine ( lines , words , bytes , label ) {
63- const format = ( number ) => String ( number ) . padStart ( 8 ) ;
64- console . log ( `${ format ( lines ) } ${ format ( words ) } ${ format ( bytes ) } ${ label } ` ) ;
55+ function printReport ( stats , options , showAll ) {
56+ const output = [ ] ;
57+ const format = ( num ) => String ( num ) . padStart ( 4 ) ;
58+
59+ if ( showAll || options . lines ) output . push ( format ( stats . lineCount ) ) ;
60+ if ( showAll || options . words ) output . push ( format ( stats . wordCount ) ) ;
61+ if ( showAll || options . bytes ) output . push ( format ( stats . byteCount ) ) ;
62+
63+ console . log ( `${ output . join ( "" ) } ${ stats . label } ` ) ;
6564}
6665
67- runWordCount ( ) ;
66+ program . parse ( process . argv ) ;
0 commit comments