Count line of code for a JavaScript project Here is my bash script that I just wrote to count line of code for JavaScript project.

It will list number of:


Comment lines
Blank lines
All lines


Here is my script:

#!/bin/bash

fileExt="*.js"

allFiles=$(find ./ -name "$fileExt")

commentLines=$((perl -ne 'print "$1\n" while /(^\s+\/\/\s*\w+)/sg' $allFiles;\
perl -0777 -ne 'print "$1\n" while /(\*\*.*?\*\/)/sg' $allFiles) | wc -l)

blankLines=$(grep '^[[:space:]]*//' -r --include $fileExt | wc -l)

allLines=$(echo $allFiles | xargs wc -l | tail -n 1 | cut -d " " -f 2)

echo -e "\nTotal comments line is: $commentLines.\n\
Total blank lines is: $blankLines.\n\
\nTotal all lines is: $allLines."


Let me explain it a little bit:

First, we need to list all files that end with ".js" within the project:

allFiles=$(find ./ -name "$fileExt")


Second, we count all comment lines:

commentLines=$((perl -ne 'print "$1\n" while /(^\s+\/\/\s*\w+)/sg' $allFiles;\
perl -0777 -ne 'print "$1\n" while /(\*\*.*?\*\/)/sg' $allFiles) | wc -l)


There are 2 types of comment lines in JavaScript:


Line start with only space and // or only //

Example:

//this is a comment line


// this is a comment line
    // this also is a comment line

All above are comment lines, and we got 3 lines total here. But the following is not comment lines:

function foo(params 1) { // return void

}


The line contains // return void is not considered as a comment, so we do not need to count it.

And for the kind of comment line. I using the regex with perl to print all comment lines that match with regex:

/(^\s+\/\/\s*\w+)/sg

Multi-line comment (JSDOC):

Example:

/**
 * return something
 * @param {object} obj
 * return void
*/


So we need to count all number of lines that start from the line with /** and end with */, we have 5 lines here.

I use this regex to match:

perl -0777 -ne 'print "$1\n" while /(\*\*.*?\*\/)/sg' $allFiles


So the total number of comment lines is the sum of two types comment lines above.


Third

We need to count blank lines.

I use the following command to match:

grep '^[[:space:]]*//' -r --include $fileExt | wc -l


Finally

We need to count all lines:

echo $allFiles | xargs wc -l | tail -n 1 | cut -d " " -f 2


I wonder if my solution is good enough or not.
fileExt="*.js"
allFiles=$(find ./ -name $fileExt)



This is a bug.  The wildcard in $fileExt will be expanded by the shell, and cause a syntax error when the current directory has more than one matching file in it:

$ touch a.js b.js
$ fileExt="*.js"
$ find ./ -name $fileExt
find: paths must precede expression: `b.js'
find: possible unquoted pattern after predicate `-name'?


You need to quote the variable.  Better yet, use the globstar option to populate an array and eliminate find altogether:

fileExt="js"
shopt -s globstar
declare -a allFiles=( **/*.$fileExt )
grep … "${allFiles[@]}" # to use the array


This regex does not match blank lines:


  grep '^[[:space:]]*//' 



Just /* is enough to start a multi-line comment in JavaScript (not /**).

The script reads every file four times.  That is slow.  Since you're using Perl already, just let it count everything.  Then you don't need to capture the file names at all, since they are used only once:

#!/bin/bash
shopt -s globstar
exec perl -nle '
    BEGIN { 
        @ARGV=grep -f, @ARGV or die "no matching files\n";
        $comment = $blank = 0;
    }
    if    ( m{ ^\s*/\* }x .. m{ \*/ }x   or   m{ ^\s*// }x ) { $comment++ }
    elsif ( m{ ^\s*$ }x ) { $blank++ }
    END { print "$comment comment lines; $blank blank lines; $. total lines" }
' **/*.js


At this point we're barely using bash anymore, and the globbing can be moved inside the Perl script, using the File::Find module:

#!/usr/bin/perl -wnl
BEGIN {
  use strict;
  use File::Find;
  my $ext="js";
  my @dirs=( @ARGV ? @ARGV : '.' );
  @ARGV=();
  find(sub { -f and /\.$ext$/o and push @ARGV, $File::Find::name }, @dirs );
  our ($comment, $blank) = (0, 0);
}

if    ( m{ ^\s*/\* }x .. m{ \*/ }x   or   m{ ^\s*// }x ) { $comment++ }
elsif ( m{ ^\s*$ }x ) { $blank++ }

END {
  print "$comment comment lines; $blank blank lines; $. total lines";
}