locio 0.1.1 → 0.1.3

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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Locio
1
+ # LocIO
2
2
 
3
3
  **A powerful CLI tool to count lines and files in directories with extensive filtering options.**
4
4
 
@@ -10,14 +10,22 @@
10
10
  - **Multiple extensions** - comma-separated extension lists (e.g., `rs,ts,js`)
11
11
  - **Size filtering** - exclude files by minimum/maximum size
12
12
  - **Binary detection** - automatically exclude binary files
13
- - **Multiple formats** - human-readable, JSON, CSV, TSV
14
- - **Fast and efficient** - built with Rust
13
+ - **Export reports** - save results as `LocIO-report.{txt,json,csv,tsv}` files
14
+ - **Fast and efficient** - built with TypeScript
15
15
  - **Rich statistics** - detailed breakdown by file extension and directory
16
16
 
17
17
  ## Quick Start
18
18
 
19
19
  ### Installation
20
20
 
21
+ ### Run with npx (no global install)
22
+
23
+ ```bash
24
+ npx locio@latest
25
+ ```
26
+
27
+ ### Install globally with npm
28
+
21
29
  ```bash
22
30
  npm install -g locio
23
31
  ```
@@ -31,20 +39,20 @@ locio
31
39
  # Count in specific directory
32
40
  locio /path/to/directory
33
41
 
34
- # Count only Rust files (dot is optional: .rs or rs both work)
35
- locio --include-ext rs --stats
42
+ # Count only TypeScript files (dot is optional: .ts or ts both work)
43
+ locio --include-ext ts,tsx --stats
36
44
  ```
37
45
 
38
46
  ## Documentation
39
47
 
40
- **[Full Documentation Available Here](https://github.com/kiron0/locio#readme)**
48
+ **[Full Documentation Available Here](https://locio.js.org)**
41
49
 
42
50
  The documentation includes:
43
51
 
44
52
  - Complete CLI reference
45
53
  - All filtering options and patterns
46
54
  - Advanced examples and use cases
47
- - Output format specifications
55
+ - Export format specifications (JSON, CSV, TSV)
48
56
  - Best practices and tips
49
57
 
50
58
  ## Basic CLI Usage
@@ -65,15 +73,15 @@ locio --exclude ".*\.log$" --exclude-dir node_modules
65
73
  # Include only specific extensions (comma-separated, dots optional)
66
74
  locio --include-ext rs,ts,js --stats
67
75
 
68
- # Output in JSON format
69
- locio --output json
76
+ # Export report in JSON format
77
+ locio --stats --export json
70
78
  ```
71
79
 
72
80
  ## Quick Example
73
81
 
74
82
  ```bash
75
- # Count Rust source files with statistics
76
- locio --include-ext rs --stats
83
+ # Count TypeScript source files with statistics
84
+ locio --include-ext ts,tsx --stats
77
85
 
78
86
  # Count TypeScript files excluding node_modules (multiple extensions supported)
79
87
  locio --include-ext ts,tsx --exclude-dir node_modules
@@ -81,12 +89,10 @@ locio --include-ext ts,tsx --exclude-dir node_modules
81
89
  # Count with size limits and binary exclusion
82
90
  locio --max-size 5MB --no-binary --stats
83
91
 
84
- # Count with JSON output
85
- locio --stats --output json > stats.json
92
+ # Export JSON report (writes to LocIO-report.json)
93
+ locio --stats --export json
86
94
  ```
87
95
 
88
96
  ---
89
97
 
90
- **[Visit the full documentation](https://github.com/kiron0/locio#readme) for complete CLI reference, examples, and tutorials.**
91
-
92
- _"Locio: Count your code, not your worries."_
98
+ _"LocIO: Count your code, not your worries."_
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ "use strict";var e=require("process"),n=require("readline"),t=require("commander"),o=require("fs"),i=require("path"),s=require("chalk"),r=require("fast-glob"),l=require("ignore");function c(e){return e&&e.__esModule?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var n=Object.create(null);return e&&Object.keys(e).forEach(function(t){if("default"!==t){var o=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,o.get?o:{enumerable:!0,get:function(){return e[t]}})}}),n.default=e,Object.freeze(n)}var d=a(e),f=a(n),u=a(o),y=a(i),_=c(s),p=c(r),x=c(l),g="0.1.2";function m(){const e=new t.Command;return e.name("LocIO").description("A powerful CLI tool to count lines and files in directories").version(g,"-v, --version","Show version number").argument("[directory]","Directory to scan",".").option("-f, --files-only","Count only files").option("-l, --lines-only","Count only lines").option("-e, --exclude <pattern>","Exclude files matching pattern",(e,n)=>n&&Array.isArray(n)?[...n,e]:[e]).option("--exclude-ext <extensions>","Exclude file extensions (comma-separated)").option("--include-ext <extensions>","Include only file extensions (comma-separated)").option("--exclude-dir <pattern>","Exclude directories matching pattern",(e,n)=>n&&Array.isArray(n)?[...n,e]:[e]).option("--include-dir <pattern>","Include only directories matching pattern",(e,n)=>n&&Array.isArray(n)?[...n,e]:[e]).option("--exclude-name <pattern>","Exclude files by name pattern",(e,n)=>n&&Array.isArray(n)?[...n,e]:[e]).option("--include-name <pattern>","Include only files by name pattern",(e,n)=>n&&Array.isArray(n)?[...n,e]:[e]).option("--max-size <size>","Maximum file size (e.g., 5MB)").option("--min-size <size>","Minimum file size (e.g., 1KB)").option("--no-hidden","Exclude hidden files").option("--no-empty","Exclude empty files").option("--follow-links","Follow symbolic links").option("--max-depth <depth>","Maximum directory depth",parseInt).option("--stats","Show detailed statistics").option("-p, --progress","Show progress").option("--include-blank","Include blank lines in count").option("--no-binary","Exclude binary files").option("-i, --ignore-case","Case-insensitive pattern matching").option("-q, --quiet","Quiet mode (minimal output)").option("--export [format]","Write report to LocIO-report.{ext} in the given format (human, json, csv, tsv)"),e}var b=class e extends Error{constructor(e,n){super(e),this.cause=n,this.name="LineCounterError"}static io(n,t){return new e(`IO error: ${n}`,t)}static invalidSizeFormat(n){return new e(`Invalid size format: ${n}`)}static invalidRegex(n,t){return new e(`Invalid regex pattern: ${n}${t?`: ${t.message}`:""}`,t)}static directoryNotFound(n){return new e(`Directory not found: ${n}`)}static notADirectory(n){return new e(`Not a directory: ${n}`)}};function h(e){return e instanceof b}function w(e){const n=["B","KB","MB","GB","TB"];let t=e,o=0;for(;t>=1024&&o<n.length-1;)t/=1024,o+=1;return 0===o?`${Math.floor(t)} ${n[o]}`:`${t.toFixed(2)} ${n[o]}`}function $(e){const n=e.directory;return"."===n?"current":n}function v(e,n){let t,o;switch(n.export||"human"){case"human":t="txt",o=function(e,n){let t="";if(n.quiet)return t+=`${e.total_files} ${e.total_lines}\n`,t;t+="=".repeat(60)+"\n",t+="LocIO RESULTS\n",t+="=".repeat(60)+"\n\n",t+=`Directory: ${$(n)}\n`,n.lines_only||(t+=`\nTotal Files: ${e.total_files}\n`,t+=`\nTotal Files Size: ${w(e.total_size)}\n`),n.files_only||(t+=`\nTotal Lines: ${e.total_lines}\n`),Object.keys(e.files_by_extension).length>0&&(t+=`\nExtensions: ${Object.keys(e.files_by_extension).sort().join(", ")}\n`);if(n.show_stats&&Object.keys(e.files_by_extension).length>0){const o=Object.keys(e.files_by_extension).sort();t+="\nStatistics by Extension:\n",t+="-".repeat(60)+"\n";for(const i of o){const o=e.files_by_extension[i],s=e.size_by_extension[i]||0,r=e.lines_by_extension[i]||0;t+=` ${i}: ${o} files`,n.lines_only||(t+=`, ${w(s)}`),n.files_only||(t+=`, ${r} lines`),t+="\n"}}if(n.show_stats&&e.details.length>0){t+="\nFiles by Directory:\n",t+="-".repeat(60)+"\n";const o={};for(const n of e.details)o[n.directory]||(o[n.directory]=[]),o[n.directory].push(n);const i=Object.keys(o).sort();for(const e of i){const i=o[e];t+=`Directory: ${e}\n`;for(const e of i){const o=w(e.size),i=null===e.lines||n.files_only?"":` | ${e.lines} lines`;t+=` - ${e.name} (${e.extension}, ${o}${i})\n`}t+="\n"}}return t+="\n",t}(e,n);break;case"json":t="json",o=function(e,n){const t={directory:$(n),files:e.total_files,size:e.total_size,size_formatted:w(e.total_size)};if(n.files_only||(t.lines=e.total_lines),n.show_stats){const n={};for(const t of Object.keys(e.files_by_extension))n[t]={files:e.files_by_extension[t],lines:e.lines_by_extension[t]||0,size:e.size_by_extension[t]||0};t.by_extension=n}return JSON.stringify(t,null,2)}(e,n);break;case"csv":t="csv",o=function(e,n){let t=`# Directory,${$(n)}\n`;t+="Extension,Files,Lines,Size\n";for(const n of Object.keys(e.files_by_extension))t+=`${n},${e.files_by_extension[n]},${e.lines_by_extension[n]||0},${e.size_by_extension[n]||0}\n`;return t}(e,n);break;case"tsv":t="tsv",o=function(e,n){let t=`# Directory\t${$(n)}\n`;t+="Extension\tFiles\tLines\tSize\n";for(const n of Object.keys(e.files_by_extension))t+=`${n}\t${e.files_by_extension[n]}\t${e.lines_by_extension[n]||0}\t${e.size_by_extension[n]||0}\n`;return t}(e,n)}const i=`LocIO-report.${t}`;try{u.writeFileSync(i,o,"utf-8"),n.quiet||console.log(`Report written to ${i}`)}catch(e){console.error(`Failed to create report file ${i}: ${e}`)}}function z(e,n){void 0===n.export?function(e,n){if(n.quiet)console.log(`${e.total_files} ${e.total_lines}`);else{if(console.log("\n"+_.default.cyan("=".repeat(60))),console.log(_.default.cyan.bold("LocIO RESULTS")),console.log(_.default.cyan("=".repeat(60))),console.log(`\n${_.default.green.bold("Directory:")} ${$(n)}`),n.lines_only||(console.log(`\n${_.default.green.bold("Total Files:")} ${_.default.yellow(e.total_files)}`),console.log(`\n${_.default.green.bold("Total Files Size:")} ${_.default.white(w(e.total_size))}`)),n.files_only||console.log(`\n${_.default.green.bold("Total Lines:")} ${_.default.yellow(e.total_lines)}`),Object.keys(e.files_by_extension).length>0){const n=Object.keys(e.files_by_extension).sort();console.log(`\n${_.default.green.bold("Extensions:")} ${_.default.white(n.join(", "))}`)}if(n.show_stats&&Object.keys(e.files_by_extension).length>0){console.log(`\n${_.default.cyan.bold("Statistics by Extension:")}`),console.log(_.default.gray("-".repeat(60)));const t=Object.keys(e.files_by_extension).sort();for(const o of t){const t=e.files_by_extension[o],i=e.size_by_extension[o]||0,s=e.lines_by_extension[o]||0;process.stdout.write(` ${_.default.white(o)}: ${_.default.yellow(t)} files`),n.lines_only||process.stdout.write(`, ${_.default.white(w(i))}`),n.files_only||process.stdout.write(`, ${_.default.yellow(s)} lines`),console.log()}}if(n.show_stats&&e.details.length>0){console.log(`\n${_.default.cyan.bold("Files by Directory:")}`),console.log(_.default.gray("-".repeat(60)));const t={};for(const n of e.details)t[n.directory]||(t[n.directory]=[]),t[n.directory].push(n);const o=Object.keys(t).sort();for(const e of o){const o=t[e];console.log(_.default.green.bold(`Directory: ${e}`));for(const e of o){const t=w(e.size),o=null===e.lines||n.files_only?"":` | ${e.lines} lines`;console.log(` - ${_.default.white(e.name)} (${_.default.blue(e.extension)}, ${_.default.white(t)}${o})`)}console.log()}}console.log()}}(e,n):v(e,n)}function S(e){const n=e.trim().toUpperCase();let t,o;n.endsWith("KB")?(t=n.slice(0,-2),o=1024):n.endsWith("MB")?(t=n.slice(0,-2),o=1048576):n.endsWith("GB")?(t=n.slice(0,-2),o=1073741824):n.endsWith("B")&&n.length>1?(t=n.slice(0,-1),o=1):(t=n,o=1);const i=parseFloat(t);return isNaN(i)?b.invalidSizeFormat(e):Math.floor(i*o)}function k(e,n){try{const t=u.readFileSync(e,"utf-8").split(/\r?\n/);return n?t.length:t.filter(e=>e.trim().length>0).length}catch(n){return b.io(`Failed to read file: ${e}`,n instanceof Error?n:void 0)}}var O=["jpg","jpeg","png","gif","bmp","svg","ico","webp","tiff","tif","psd","raw","heic","heif","avif","mp3","wav","flac","aac","ogg","wma","m4a","opus","aiff","au","mp4","avi","mkv","mov","wmv","flv","webm","m4v","mpg","mpeg","3gp","ogv","zip","rar","7z","tar","gz","bz2","xz","z","cab","iso","dmg","pkg","deb","rpm","pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","exe","dll","so","dylib","app","msi","bin","run","ttf","otf","woff","woff2","eot","db","sqlite","sqlite3","mdb","accdb","ps","eps","ai","sketch","fig","xd"];function E(e,n,t){const o=e,i=y.basename(e);for(const e of t.exclude_patterns)if(e.test(o))return!0;const s=y.extname(e).replace(/^\./,"").toLowerCase();if(s){for(const e of t.exclude_extensions)if(s===e.toLowerCase())return!0;if(t.include_extensions.length>0){let e=!1;for(const n of t.include_extensions)if(s===n.toLowerCase()){e=!0;break}if(!e)return!0}}else if(t.include_extensions.length>0)return!0;const r=y.dirname(e);for(const e of t.exclude_dirs)if(e.test(r))return!0;if(t.include_dirs.length>0){let e=!1;for(const n of t.include_dirs)if(n.test(r)){e=!0;break}if(!e)return!0}for(const e of t.exclude_names)if(e.test(i))return!0;if(t.include_names.length>0){let e=!1;for(const n of t.include_names)if(n.test(i)){e=!0;break}if(!e)return!0}if(n.no_hidden&&i.startsWith("."))return!0;try{const t=u.statSync(e).size;if(n.max_size){const e=S(n.max_size);if(!(e instanceof b)&&t>e)return!0}if(n.min_size){const e=S(n.min_size);if(!(e instanceof b)&&t<e)return!0}if(n.no_empty&&0===t)return!0}catch{}return!(!n.no_binary||!function(e){try{const n=Buffer.alloc(8192),t=u.openSync(e,"r"),o=u.readSync(t,n,0,8192,0);if(u.closeSync(t),0===o)return!1;for(let e=0;e<o;e++)if(0===n[e])return!0;return!1}catch{return!1}}(e))}function L(e){const n=Date.now(),t={total_files:0,total_lines:0,total_size:0,files_by_extension:{},lines_by_extension:{},size_by_extension:{},details:[]},o=function(e){try{const n=e.exclude_patterns.map(n=>{try{return new RegExp(n,e.ignore_case?"i":void 0)}catch(e){throw b.invalidRegex(n,e instanceof Error?e:void 0)}}),t=[...e.exclude_extensions.map(e=>e.replace(/^\./,"").toLowerCase()),...O.map(e=>e.toLowerCase())],o=Array.from(new Set(t)).sort(),i=e.include_extensions.map(e=>e.replace(/^\./,"").toLowerCase()),s=e.exclude_dirs.map(n=>new RegExp(n,e.ignore_case?"i":void 0)),r=e.include_dirs.map(n=>new RegExp(n,e.ignore_case?"i":void 0));return{exclude_patterns:n,exclude_extensions:o,include_extensions:i,exclude_dirs:s,include_dirs:r,exclude_names:e.exclude_names.map(n=>new RegExp(n,e.ignore_case?"i":void 0)),include_names:e.include_names.map(n=>new RegExp(n,e.ignore_case?"i":void 0))}}catch(e){return e instanceof b?e:b.io(`Failed to create filter patterns: ${e instanceof Error?e.message:String(e)}`)}}(e);if(h(o))return o;const i=function(e){const n=x.default();return n.add(".git"),n.add(".gitignore"),n.add(".lcignore"),function e(t,o){const i=y.join(t,".gitignore");try{if(u.existsSync(i)&&u.statSync(i).isFile()){const e=u.readFileSync(i,"utf-8"),s=y.relative(o,t)||".",r=e.split("\n").map(e=>e.trim()).filter(e=>e&&!e.startsWith("#"));for(const e of r)"."===s?n.add(e):n.add(y.join(s,e))}}catch{}try{const n=u.readdirSync(t);for(const i of n){const n=y.join(t,i);try{u.statSync(n).isDirectory()&&".git"!==i&&e(n,o)}catch{}}}catch{}}(e,e),n}(e.directory),s=y.join(e.directory,"**/*"),r={cwd:e.directory,absolute:!0,onlyFiles:!0,ignore:[],dot:!e.no_hidden,followSymbolicLinks:e.follow_links};let l=0,c=0;try{const n=p.default.sync(s,r).map(e=>"string"==typeof e?e:e.path||String(e));for(const s of n){if(void 0!==e.max_depth){const n=y.relative(e.directory,s);if(n.split(y.sep).length-1>e.max_depth)continue}const n=y.relative(e.directory,s);if(i.ignores(n))continue;try{if(!u.statSync(s).isFile())continue}catch{continue}if(E(s,e,o))continue;let r;l+=1,e.show_progress&&l%100==0&&!e.quiet&&process.stderr.write(`\rProcessed: ${l} files...`);try{r=u.statSync(s)}catch(n){e.quiet||console.error(`Warning: Could not read metadata for ${s}: ${n}`),c+=1;continue}const a=r.size,d=y.extname(s).replace(/^\./,"").toLowerCase()||"no-ext";t.total_files+=1,t.total_size+=a,t.files_by_extension[d]=(t.files_by_extension[d]||0)+1,t.size_by_extension[d]=(t.size_by_extension[d]||0)+a;let f=null;if(!e.files_only){const n=k(s,e.include_blank);h(n)?(e.quiet||console.error(`Warning: Could not count lines in ${s}: ${n.message}`),c+=1):(f=n,t.total_lines+=n,t.lines_by_extension[d]=(t.lines_by_extension[d]||0)+n)}const _=y.dirname(s),p=y.basename(s);t.details.push({directory:_,name:p,extension:d,size:a,lines:f})}}catch(e){return b.io(`Failed to scan directory: ${e instanceof Error?e.message:String(e)}`,e instanceof Error?e:void 0)}if(e.show_progress&&!e.quiet){const e=`${Date.now()-n}ms`;process.stderr.write(`\rProcessed: ${l} files (${c} errors) in ${e}\n`)}return t}function j(e){const n=function(e){if(e.version)return void console.log(`LocIO ${g}`);const n=y.resolve(e.directory);if(!u.existsSync(n))return b.directoryNotFound(e.directory);if(!u.statSync(n).isDirectory())return b.notADirectory(e.directory);const t=L({...e,directory:n});if(h(t))return t;z(t,{...e,directory:n})}(e);h(n)&&(console.error(`Error: ${n.message}`),process.exit(1)),process.exit(0)}(async function(){if(2===d.argv.length&&d.stdin.isTTY){if(!await new Promise((e,n)=>{const t=g;console.log("===================================="),console.log(` LocIO CLI v${t}`),console.log("===================================="),console.log("A fast, flexible line and file counter for your projects.\n"),console.log("Select an option:"),console.log(" 1) Quick scan of current directory (default settings)"),console.log(" 2) Show common command examples"),console.log(" 3) View full help (same as --help)"),console.log(" q) Quit\n"),d.nextTick(()=>{const t=f.createInterface({input:d.stdin,output:d.stdout}),o=()=>{t.question("Enter choice (1/2/3/q): ",n=>{switch(n.trim()){case"1":console.log('\nRunning quick scan on current directory (".") with default settings...\n'),t.close(),e(!0);break;case"2":console.log("\nCommon commands:"),console.log(" LocIO . --files-only"),console.log(" Scan current directory with default settings."),console.log(" LocIO . --files-only"),console.log(" Show only file counts."),console.log(" LocIO . --lines-only"),console.log(" Show only line counts."),console.log(' LocIO . --exclude "target" --exclude-dir ".git"'),console.log(" Ignore build and VCS directories."),console.log(" LocIO . --include-ext rs,ts,tsx"),console.log(" Only include specific extensions."),console.log(" LocIO . --export json"),console.log(" Print results in JSON format.\n"),t.close(),e(!1);break;case"3":console.log(),m().outputHelp(),console.log("\n"),t.close(),e(!1);break;case"q":case"Q":console.log("\nThank you for using LocIO."),t.close(),e(!1);break;default:console.log("Invalid choice. Please enter 1, 2, 3, or q.\n"),o()}})};t.on("error",e=>{console.error("\nFailed to read input. Exiting."),t.close(),n(e)}),o()})}))return}j(function(){const e=m();e.parse();const n=e.opts(),t=e.args,o=n.excludeExt?n.excludeExt.split(",").map(e=>e.trim()):[],i=n.includeExt?n.includeExt.split(",").map(e=>e.trim()):[];return{directory:t[0]||".",files_only:n.filesOnly||!1,lines_only:n.linesOnly||!1,exclude_patterns:n.exclude||[],exclude_extensions:o,include_extensions:i,exclude_dirs:n.excludeDir||[],include_dirs:n.includeDir||[],exclude_names:n.excludeName||[],include_names:n.includeName||[],max_size:n.maxSize,min_size:n.minSize,no_hidden:n.noHidden||!1,no_empty:n.noEmpty||!1,follow_links:n.followLinks||!1,max_depth:n.maxDepth,show_stats:n.stats||!1,show_progress:n.progress||!1,include_blank:n.includeBlank||!1,no_binary:n.noBinary||!1,ignore_case:n.ignoreCase||!1,quiet:n.quiet||!1,export:void 0!==n.export?!0===n.export?"human":"json"===n.export.toLowerCase()?"json":"csv"===n.export.toLowerCase()?"csv":"tsv"===n.export.toLowerCase()?"tsv":"human":void 0,version:!1}}())})().catch(e=>{console.error("Unexpected error:",e),d.exit(1)});
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import*as e from"process";import*as o from"readline";import{Command as n}from"commander";import*as t from"fs";import*as i from"path";import s from"chalk";import r from"fast-glob";import l from"ignore";var c="0.1.2";function a(){const e=new n;return e.name("LocIO").description("A powerful CLI tool to count lines and files in directories").version(c,"-v, --version","Show version number").argument("[directory]","Directory to scan",".").option("-f, --files-only","Count only files").option("-l, --lines-only","Count only lines").option("-e, --exclude <pattern>","Exclude files matching pattern",(e,o)=>o&&Array.isArray(o)?[...o,e]:[e]).option("--exclude-ext <extensions>","Exclude file extensions (comma-separated)").option("--include-ext <extensions>","Include only file extensions (comma-separated)").option("--exclude-dir <pattern>","Exclude directories matching pattern",(e,o)=>o&&Array.isArray(o)?[...o,e]:[e]).option("--include-dir <pattern>","Include only directories matching pattern",(e,o)=>o&&Array.isArray(o)?[...o,e]:[e]).option("--exclude-name <pattern>","Exclude files by name pattern",(e,o)=>o&&Array.isArray(o)?[...o,e]:[e]).option("--include-name <pattern>","Include only files by name pattern",(e,o)=>o&&Array.isArray(o)?[...o,e]:[e]).option("--max-size <size>","Maximum file size (e.g., 5MB)").option("--min-size <size>","Minimum file size (e.g., 1KB)").option("--no-hidden","Exclude hidden files").option("--no-empty","Exclude empty files").option("--follow-links","Follow symbolic links").option("--max-depth <depth>","Maximum directory depth",parseInt).option("--stats","Show detailed statistics").option("-p, --progress","Show progress").option("--include-blank","Include blank lines in count").option("--no-binary","Exclude binary files").option("-i, --ignore-case","Case-insensitive pattern matching").option("-q, --quiet","Quiet mode (minimal output)").option("--export [format]","Write report to LocIO-report.{ext} in the given format (human, json, csv, tsv)"),e}var f=class e extends Error{constructor(e,o){super(e),this.cause=o,this.name="LineCounterError"}static io(o,n){return new e(`IO error: ${o}`,n)}static invalidSizeFormat(o){return new e(`Invalid size format: ${o}`)}static invalidRegex(o,n){return new e(`Invalid regex pattern: ${o}${n?`: ${n.message}`:""}`,n)}static directoryNotFound(o){return new e(`Directory not found: ${o}`)}static notADirectory(o){return new e(`Not a directory: ${o}`)}};function d(e){return e instanceof f}function u(e){const o=["B","KB","MB","GB","TB"];let n=e,t=0;for(;n>=1024&&t<o.length-1;)n/=1024,t+=1;return 0===t?`${Math.floor(n)} ${o[t]}`:`${n.toFixed(2)} ${o[t]}`}function y(e){const o=e.directory;return"."===o?"current":o}function p(e,o){let n,i;switch(o.export||"human"){case"human":n="txt",i=function(e,o){let n="";if(o.quiet)return n+=`${e.total_files} ${e.total_lines}\n`,n;n+="=".repeat(60)+"\n",n+="LocIO RESULTS\n",n+="=".repeat(60)+"\n\n",n+=`Directory: ${y(o)}\n`,o.lines_only||(n+=`\nTotal Files: ${e.total_files}\n`,n+=`\nTotal Files Size: ${u(e.total_size)}\n`),o.files_only||(n+=`\nTotal Lines: ${e.total_lines}\n`),Object.keys(e.files_by_extension).length>0&&(n+=`\nExtensions: ${Object.keys(e.files_by_extension).sort().join(", ")}\n`);if(o.show_stats&&Object.keys(e.files_by_extension).length>0){const t=Object.keys(e.files_by_extension).sort();n+="\nStatistics by Extension:\n",n+="-".repeat(60)+"\n";for(const i of t){const t=e.files_by_extension[i],s=e.size_by_extension[i]||0,r=e.lines_by_extension[i]||0;n+=` ${i}: ${t} files`,o.lines_only||(n+=`, ${u(s)}`),o.files_only||(n+=`, ${r} lines`),n+="\n"}}if(o.show_stats&&e.details.length>0){n+="\nFiles by Directory:\n",n+="-".repeat(60)+"\n";const t={};for(const o of e.details)t[o.directory]||(t[o.directory]=[]),t[o.directory].push(o);const i=Object.keys(t).sort();for(const e of i){const i=t[e];n+=`Directory: ${e}\n`;for(const e of i){const t=u(e.size),i=null===e.lines||o.files_only?"":` | ${e.lines} lines`;n+=` - ${e.name} (${e.extension}, ${t}${i})\n`}n+="\n"}}return n+="\n",n}(e,o);break;case"json":n="json",i=function(e,o){const n={directory:y(o),files:e.total_files,size:e.total_size,size_formatted:u(e.total_size)};if(o.files_only||(n.lines=e.total_lines),o.show_stats){const o={};for(const n of Object.keys(e.files_by_extension))o[n]={files:e.files_by_extension[n],lines:e.lines_by_extension[n]||0,size:e.size_by_extension[n]||0};n.by_extension=o}return JSON.stringify(n,null,2)}(e,o);break;case"csv":n="csv",i=function(e,o){let n=`# Directory,${y(o)}\n`;n+="Extension,Files,Lines,Size\n";for(const o of Object.keys(e.files_by_extension))n+=`${o},${e.files_by_extension[o]},${e.lines_by_extension[o]||0},${e.size_by_extension[o]||0}\n`;return n}(e,o);break;case"tsv":n="tsv",i=function(e,o){let n=`# Directory\t${y(o)}\n`;n+="Extension\tFiles\tLines\tSize\n";for(const o of Object.keys(e.files_by_extension))n+=`${o}\t${e.files_by_extension[o]}\t${e.lines_by_extension[o]||0}\t${e.size_by_extension[o]||0}\n`;return n}(e,o)}const s=`LocIO-report.${n}`;try{t.writeFileSync(s,i,"utf-8"),o.quiet||console.log(`Report written to ${s}`)}catch(e){console.error(`Failed to create report file ${s}: ${e}`)}}function _(e,o){void 0===o.export?function(e,o){if(o.quiet)console.log(`${e.total_files} ${e.total_lines}`);else{if(console.log("\n"+s.cyan("=".repeat(60))),console.log(s.cyan.bold("LocIO RESULTS")),console.log(s.cyan("=".repeat(60))),console.log(`\n${s.green.bold("Directory:")} ${y(o)}`),o.lines_only||(console.log(`\n${s.green.bold("Total Files:")} ${s.yellow(e.total_files)}`),console.log(`\n${s.green.bold("Total Files Size:")} ${s.white(u(e.total_size))}`)),o.files_only||console.log(`\n${s.green.bold("Total Lines:")} ${s.yellow(e.total_lines)}`),Object.keys(e.files_by_extension).length>0){const o=Object.keys(e.files_by_extension).sort();console.log(`\n${s.green.bold("Extensions:")} ${s.white(o.join(", "))}`)}if(o.show_stats&&Object.keys(e.files_by_extension).length>0){console.log(`\n${s.cyan.bold("Statistics by Extension:")}`),console.log(s.gray("-".repeat(60)));const n=Object.keys(e.files_by_extension).sort();for(const t of n){const n=e.files_by_extension[t],i=e.size_by_extension[t]||0,r=e.lines_by_extension[t]||0;process.stdout.write(` ${s.white(t)}: ${s.yellow(n)} files`),o.lines_only||process.stdout.write(`, ${s.white(u(i))}`),o.files_only||process.stdout.write(`, ${s.yellow(r)} lines`),console.log()}}if(o.show_stats&&e.details.length>0){console.log(`\n${s.cyan.bold("Files by Directory:")}`),console.log(s.gray("-".repeat(60)));const n={};for(const o of e.details)n[o.directory]||(n[o.directory]=[]),n[o.directory].push(o);const t=Object.keys(n).sort();for(const e of t){const t=n[e];console.log(s.green.bold(`Directory: ${e}`));for(const e of t){const n=u(e.size),t=null===e.lines||o.files_only?"":` | ${e.lines} lines`;console.log(` - ${s.white(e.name)} (${s.blue(e.extension)}, ${s.white(n)}${t})`)}console.log()}}console.log()}}(e,o):p(e,o)}function x(e){const o=e.trim().toUpperCase();let n,t;o.endsWith("KB")?(n=o.slice(0,-2),t=1024):o.endsWith("MB")?(n=o.slice(0,-2),t=1048576):o.endsWith("GB")?(n=o.slice(0,-2),t=1073741824):o.endsWith("B")&&o.length>1?(n=o.slice(0,-1),t=1):(n=o,t=1);const i=parseFloat(n);return isNaN(i)?f.invalidSizeFormat(e):Math.floor(i*t)}function g(e,o){try{const n=t.readFileSync(e,"utf-8").split(/\r?\n/);return o?n.length:n.filter(e=>e.trim().length>0).length}catch(o){return f.io(`Failed to read file: ${e}`,o instanceof Error?o:void 0)}}var m=["jpg","jpeg","png","gif","bmp","svg","ico","webp","tiff","tif","psd","raw","heic","heif","avif","mp3","wav","flac","aac","ogg","wma","m4a","opus","aiff","au","mp4","avi","mkv","mov","wmv","flv","webm","m4v","mpg","mpeg","3gp","ogv","zip","rar","7z","tar","gz","bz2","xz","z","cab","iso","dmg","pkg","deb","rpm","pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","exe","dll","so","dylib","app","msi","bin","run","ttf","otf","woff","woff2","eot","db","sqlite","sqlite3","mdb","accdb","ps","eps","ai","sketch","fig","xd"];function b(e,o,n){const s=e,r=i.basename(e);for(const e of n.exclude_patterns)if(e.test(s))return!0;const l=i.extname(e).replace(/^\./,"").toLowerCase();if(l){for(const e of n.exclude_extensions)if(l===e.toLowerCase())return!0;if(n.include_extensions.length>0){let e=!1;for(const o of n.include_extensions)if(l===o.toLowerCase()){e=!0;break}if(!e)return!0}}else if(n.include_extensions.length>0)return!0;const c=i.dirname(e);for(const e of n.exclude_dirs)if(e.test(c))return!0;if(n.include_dirs.length>0){let e=!1;for(const o of n.include_dirs)if(o.test(c)){e=!0;break}if(!e)return!0}for(const e of n.exclude_names)if(e.test(r))return!0;if(n.include_names.length>0){let e=!1;for(const o of n.include_names)if(o.test(r)){e=!0;break}if(!e)return!0}if(o.no_hidden&&r.startsWith("."))return!0;try{const n=t.statSync(e).size;if(o.max_size){const e=x(o.max_size);if(!(e instanceof f)&&n>e)return!0}if(o.min_size){const e=x(o.min_size);if(!(e instanceof f)&&n<e)return!0}if(o.no_empty&&0===n)return!0}catch{}return!(!o.no_binary||!function(e){try{const o=Buffer.alloc(8192),n=t.openSync(e,"r"),i=t.readSync(n,o,0,8192,0);if(t.closeSync(n),0===i)return!1;for(let e=0;e<i;e++)if(0===o[e])return!0;return!1}catch{return!1}}(e))}function h(e){const o=Date.now(),n={total_files:0,total_lines:0,total_size:0,files_by_extension:{},lines_by_extension:{},size_by_extension:{},details:[]},s=function(e){try{const o=e.exclude_patterns.map(o=>{try{return new RegExp(o,e.ignore_case?"i":void 0)}catch(e){throw f.invalidRegex(o,e instanceof Error?e:void 0)}}),n=[...e.exclude_extensions.map(e=>e.replace(/^\./,"").toLowerCase()),...m.map(e=>e.toLowerCase())],t=Array.from(new Set(n)).sort(),i=e.include_extensions.map(e=>e.replace(/^\./,"").toLowerCase()),s=e.exclude_dirs.map(o=>new RegExp(o,e.ignore_case?"i":void 0)),r=e.include_dirs.map(o=>new RegExp(o,e.ignore_case?"i":void 0));return{exclude_patterns:o,exclude_extensions:t,include_extensions:i,exclude_dirs:s,include_dirs:r,exclude_names:e.exclude_names.map(o=>new RegExp(o,e.ignore_case?"i":void 0)),include_names:e.include_names.map(o=>new RegExp(o,e.ignore_case?"i":void 0))}}catch(e){return e instanceof f?e:f.io(`Failed to create filter patterns: ${e instanceof Error?e.message:String(e)}`)}}(e);if(d(s))return s;const c=function(e){const o=l();return o.add(".git"),o.add(".gitignore"),o.add(".lcignore"),function e(n,s){const r=i.join(n,".gitignore");try{if(t.existsSync(r)&&t.statSync(r).isFile()){const e=t.readFileSync(r,"utf-8"),l=i.relative(s,n)||".",c=e.split("\n").map(e=>e.trim()).filter(e=>e&&!e.startsWith("#"));for(const e of c)"."===l?o.add(e):o.add(i.join(l,e))}}catch{}try{const o=t.readdirSync(n);for(const r of o){const o=i.join(n,r);try{t.statSync(o).isDirectory()&&".git"!==r&&e(o,s)}catch{}}}catch{}}(e,e),o}(e.directory),a=i.join(e.directory,"**/*"),u={cwd:e.directory,absolute:!0,onlyFiles:!0,ignore:[],dot:!e.no_hidden,followSymbolicLinks:e.follow_links};let y=0,p=0;try{const o=r.sync(a,u).map(e=>"string"==typeof e?e:e.path||String(e));for(const r of o){if(void 0!==e.max_depth){const o=i.relative(e.directory,r);if(o.split(i.sep).length-1>e.max_depth)continue}const o=i.relative(e.directory,r);if(c.ignores(o))continue;try{if(!t.statSync(r).isFile())continue}catch{continue}if(b(r,e,s))continue;let l;y+=1,e.show_progress&&y%100==0&&!e.quiet&&process.stderr.write(`\rProcessed: ${y} files...`);try{l=t.statSync(r)}catch(o){e.quiet||console.error(`Warning: Could not read metadata for ${r}: ${o}`),p+=1;continue}const a=l.size,f=i.extname(r).replace(/^\./,"").toLowerCase()||"no-ext";n.total_files+=1,n.total_size+=a,n.files_by_extension[f]=(n.files_by_extension[f]||0)+1,n.size_by_extension[f]=(n.size_by_extension[f]||0)+a;let u=null;if(!e.files_only){const o=g(r,e.include_blank);d(o)?(e.quiet||console.error(`Warning: Could not count lines in ${r}: ${o.message}`),p+=1):(u=o,n.total_lines+=o,n.lines_by_extension[f]=(n.lines_by_extension[f]||0)+o)}const _=i.dirname(r),x=i.basename(r);n.details.push({directory:_,name:x,extension:f,size:a,lines:u})}}catch(e){return f.io(`Failed to scan directory: ${e instanceof Error?e.message:String(e)}`,e instanceof Error?e:void 0)}if(e.show_progress&&!e.quiet){const e=`${Date.now()-o}ms`;process.stderr.write(`\rProcessed: ${y} files (${p} errors) in ${e}\n`)}return n}function $(e){const o=function(e){if(e.version)return void console.log(`LocIO ${c}`);const o=i.resolve(e.directory);if(!t.existsSync(o))return f.directoryNotFound(e.directory);if(!t.statSync(o).isDirectory())return f.notADirectory(e.directory);const n=h({...e,directory:o});if(d(n))return n;_(n,{...e,directory:o})}(e);d(o)&&(console.error(`Error: ${o.message}`),process.exit(1)),process.exit(0)}(async function(){if(2===e.argv.length&&e.stdin.isTTY){if(!await new Promise((n,t)=>{const i=c;console.log("===================================="),console.log(` LocIO CLI v${i}`),console.log("===================================="),console.log("A fast, flexible line and file counter for your projects.\n"),console.log("Select an option:"),console.log(" 1) Quick scan of current directory (default settings)"),console.log(" 2) Show common command examples"),console.log(" 3) View full help (same as --help)"),console.log(" q) Quit\n"),e.nextTick(()=>{const i=o.createInterface({input:e.stdin,output:e.stdout}),s=()=>{i.question("Enter choice (1/2/3/q): ",e=>{switch(e.trim()){case"1":console.log('\nRunning quick scan on current directory (".") with default settings...\n'),i.close(),n(!0);break;case"2":console.log("\nCommon commands:"),console.log(" LocIO . --files-only"),console.log(" Scan current directory with default settings."),console.log(" LocIO . --files-only"),console.log(" Show only file counts."),console.log(" LocIO . --lines-only"),console.log(" Show only line counts."),console.log(' LocIO . --exclude "target" --exclude-dir ".git"'),console.log(" Ignore build and VCS directories."),console.log(" LocIO . --include-ext rs,ts,tsx"),console.log(" Only include specific extensions."),console.log(" LocIO . --export json"),console.log(" Print results in JSON format.\n"),i.close(),n(!1);break;case"3":console.log(),a().outputHelp(),console.log("\n"),i.close(),n(!1);break;case"q":case"Q":console.log("\nThank you for using LocIO."),i.close(),n(!1);break;default:console.log("Invalid choice. Please enter 1, 2, 3, or q.\n"),s()}})};i.on("error",e=>{console.error("\nFailed to read input. Exiting."),i.close(),t(e)}),s()})}))return}$(function(){const e=a();e.parse();const o=e.opts(),n=e.args,t=o.excludeExt?o.excludeExt.split(",").map(e=>e.trim()):[],i=o.includeExt?o.includeExt.split(",").map(e=>e.trim()):[];return{directory:n[0]||".",files_only:o.filesOnly||!1,lines_only:o.linesOnly||!1,exclude_patterns:o.exclude||[],exclude_extensions:t,include_extensions:i,exclude_dirs:o.excludeDir||[],include_dirs:o.includeDir||[],exclude_names:o.excludeName||[],include_names:o.includeName||[],max_size:o.maxSize,min_size:o.minSize,no_hidden:o.noHidden||!1,no_empty:o.noEmpty||!1,follow_links:o.followLinks||!1,max_depth:o.maxDepth,show_stats:o.stats||!1,show_progress:o.progress||!1,include_blank:o.includeBlank||!1,no_binary:o.noBinary||!1,ignore_case:o.ignoreCase||!1,quiet:o.quiet||!1,export:void 0!==o.export?!0===o.export?"human":"json"===o.export.toLowerCase()?"json":"csv"===o.export.toLowerCase()?"csv":"tsv"===o.export.toLowerCase()?"tsv":"human":void 0,version:!1}}())})().catch(o=>{console.error("Unexpected error:",o),e.exit(1)});
package/package.json CHANGED
@@ -1,22 +1,20 @@
1
1
  {
2
2
  "name": "locio",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A powerful CLI tool to count lines and files in directories with extensive filtering options",
5
+ "type": "module",
6
+ "main": "dist/index.js",
5
7
  "bin": {
6
- "lc": "target/release/locio",
7
- "locio": "target/release/locio"
8
+ "locio": "./dist/index.js"
8
9
  },
9
10
  "scripts": {
10
- "start": "./target/release/locio",
11
- "build": "source $HOME/.cargo/env && cargo build --release",
12
- "prepublishOnly": "npm run build",
13
- "version:check": "./scripts/version.sh",
14
- "version:update": "./scripts/npm-version.sh",
15
- "format": "npx prettier --write \"**/*.{js,json,ts,md}\""
11
+ "build": "rm -rf dist && tsup",
12
+ "build:watch": "tsup --watch",
13
+ "format": "prettier --write \"**/*.{js,json,ts,md}\""
16
14
  },
17
15
  "keywords": [
18
16
  "cli",
19
- "locio",
17
+ "LocIO",
20
18
  "file-counter",
21
19
  "statistics",
22
20
  "code-analysis",
@@ -26,12 +24,26 @@
26
24
  "author": "Toufiq Hasan Kiron <hello@kiron.dev>",
27
25
  "license": "MIT",
28
26
  "files": [
29
- "target/release/locio",
30
- "target/release/lc"
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
31
30
  ],
32
31
  "repository": {
33
32
  "type": "git",
34
33
  "url": "https://github.com/kiron0/locio.git"
35
34
  },
36
- "homepage": "https://github.com/kiron0/locio#readme"
35
+ "homepage": "https://locio.js.org",
36
+ "dependencies": {
37
+ "commander": "^14.0.2",
38
+ "chalk": "^5.6.2",
39
+ "ignore": "^7.0.5",
40
+ "fast-glob": "^3.3.3"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^25.0.3",
44
+ "prettier": "^3.7.4",
45
+ "terser": "^5.44.1",
46
+ "tsup": "^8.5.1",
47
+ "typescript": "^5.9.3"
48
+ }
37
49
  }
Binary file