Skip to content

Commit 04bea92

Browse files
committed
Implement wc in JavaScript
1 parent 06a608c commit 04bea92

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

implement-shell-tools/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}

implement-shell-tools/wc/wc.mjs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import process from "node:process";
2+
import { promises as fs } from "node:fs";
3+
import { parseArgs } from "node:util";
4+
5+
const { values, positionals } = parseArgs({
6+
options: {
7+
lines: { type: "boolean", short: "l", default: false },
8+
words: { type: "boolean", short: "w", default: false },
9+
bytes: { type: "boolean", short: "c", default: false },
10+
},
11+
allowPositionals: true,
12+
});
13+
14+
const noFlags = !values.lines && !values.words && !values.bytes;
15+
const showLines = noFlags || values.lines;
16+
const showWords = noFlags || values.words;
17+
const showBytes = noFlags || values.bytes;
18+
19+
if (positionals.length === 0) {
20+
console.error("Usage: node wc.mjs [-l] [-w] [-c] <path>...");
21+
process.exit(1);
22+
}
23+
24+
function countLines(content) {
25+
let count = 0;
26+
for (const character of content) {
27+
if (character === "\n") {
28+
count++;
29+
}
30+
}
31+
return count;
32+
}
33+
34+
function countWords(content) {
35+
return content.split(/\s+/).filter((word) => word !== "").length;
36+
}
37+
38+
const results = [];
39+
const totals = { lines: 0, words: 0, bytes: 0 };
40+
41+
for (const path of positionals) {
42+
let buffer;
43+
try {
44+
buffer = await fs.readFile(path);
45+
} catch {
46+
console.error(`wc: ${path}: No such file or directory`);
47+
process.exitCode = 1;
48+
continue;
49+
}
50+
51+
const content = buffer.toString("utf-8");
52+
const counts = {
53+
lines: countLines(content),
54+
words: countWords(content),
55+
bytes: buffer.length,
56+
name: path,
57+
};
58+
59+
results.push(counts);
60+
totals.lines += counts.lines;
61+
totals.words += counts.words;
62+
totals.bytes += counts.bytes;
63+
}
64+
65+
if (results.length > 1) {
66+
results.push({ ...totals, name: "total" });
67+
}
68+
69+
const selectedCounts = [showLines, showWords, showBytes].filter(Boolean).length;
70+
const skipPadding = selectedCounts === 1 && results.length === 1;
71+
72+
let width = 1;
73+
if (!skipPadding) {
74+
for (const result of results) {
75+
for (const key of ["lines", "words", "bytes"]) {
76+
width = Math.max(width, String(result[key]).length);
77+
}
78+
}
79+
}
80+
81+
for (const result of results) {
82+
const columns = [];
83+
if (showLines) columns.push(String(result.lines).padStart(width));
84+
if (showWords) columns.push(String(result.words).padStart(width));
85+
if (showBytes) columns.push(String(result.bytes).padStart(width));
86+
console.log(`${columns.join(" ")} ${result.name}`);
87+
}

0 commit comments

Comments
 (0)