@dev-pi2pie/word-counter 0.1.0-canary.4 → 0.1.0
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 +113 -70
- package/dist/cjs/index.cjs +104 -22
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/bin.mjs +1216 -784
- package/dist/esm/bin.mjs.map +1 -1
- package/dist/esm/index.d.mts +9 -2
- package/dist/esm/index.mjs +104 -22
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +9 -6
package/dist/esm/bin.mjs
CHANGED
|
@@ -1,37 +1,759 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, statSync } from "node:fs";
|
|
5
|
+
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
8
|
+
import { parseDocument } from "yaml";
|
|
9
|
+
|
|
10
|
+
//#region \0rolldown/runtime.js
|
|
11
|
+
var __create = Object.create;
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
16
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
17
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
21
|
+
key = keys[i];
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
23
|
+
__defProp(to, key, {
|
|
24
|
+
get: ((k) => from[k]).bind(null, key),
|
|
25
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
33
|
+
value: mod,
|
|
34
|
+
enumerable: true
|
|
35
|
+
}) : target, mod));
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/cli/debug/channel.ts
|
|
39
|
+
const NOOP_CLOSE = async () => {};
|
|
40
|
+
function shouldEmitAtVerbosity(channelVerbosity, eventVerbosity) {
|
|
41
|
+
return channelVerbosity === "verbose" || eventVerbosity === "compact";
|
|
42
|
+
}
|
|
43
|
+
function formatTimestampPart(value) {
|
|
44
|
+
return String(value).padStart(2, "0");
|
|
45
|
+
}
|
|
46
|
+
function formatDebugReportTimestamp(now) {
|
|
47
|
+
return [`${now.getFullYear()}${formatTimestampPart(now.getMonth() + 1)}${formatTimestampPart(now.getDate())}`, `${formatTimestampPart(now.getHours())}${formatTimestampPart(now.getMinutes())}${formatTimestampPart(now.getSeconds())}`].join("-");
|
|
48
|
+
}
|
|
49
|
+
function withCollisionSuffix(pathValue, sequence) {
|
|
50
|
+
if (sequence <= 0) return pathValue;
|
|
51
|
+
const extension = extname(pathValue);
|
|
52
|
+
const baseName = basename(pathValue, extension);
|
|
53
|
+
return join(dirname(pathValue), `${baseName}-${sequence}${extension}`);
|
|
54
|
+
}
|
|
55
|
+
function resolveReportPath(report, now, pid) {
|
|
56
|
+
const cwd = report.cwd ?? process.cwd();
|
|
57
|
+
const defaultName = `wc-debug-${formatDebugReportTimestamp(now)}-${pid}.jsonl`;
|
|
58
|
+
const explicitPath = typeof report.path === "string";
|
|
59
|
+
const basePath = explicitPath ? resolve(cwd, report.path) : resolve(cwd, defaultName);
|
|
60
|
+
mkdirSync(dirname(basePath), { recursive: true });
|
|
61
|
+
if (explicitPath) {
|
|
62
|
+
if (existsSync(basePath) && statSync(basePath).isDirectory()) throw new Error(`debug report path must be a file: ${basePath}`);
|
|
63
|
+
return basePath;
|
|
64
|
+
}
|
|
65
|
+
let candidate = basePath;
|
|
66
|
+
let sequence = 0;
|
|
67
|
+
while (existsSync(candidate)) {
|
|
68
|
+
sequence += 1;
|
|
69
|
+
candidate = withCollisionSuffix(basePath, sequence);
|
|
70
|
+
}
|
|
71
|
+
return candidate;
|
|
72
|
+
}
|
|
73
|
+
function createTerminalSink() {
|
|
74
|
+
return {
|
|
75
|
+
write(line) {
|
|
76
|
+
console.error(`[debug] ${line}`);
|
|
77
|
+
},
|
|
78
|
+
close: NOOP_CLOSE
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createFileSink(pathValue) {
|
|
82
|
+
try {
|
|
83
|
+
closeSync(openSync(pathValue, "a"));
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
throw new Error(`debug report path is not writable: ${pathValue} (${message})`);
|
|
87
|
+
}
|
|
88
|
+
const stream = createWriteStream(pathValue, {
|
|
89
|
+
flags: "a",
|
|
90
|
+
encoding: "utf8"
|
|
91
|
+
});
|
|
92
|
+
let streamError;
|
|
93
|
+
stream.on("error", (error) => {
|
|
94
|
+
streamError = error;
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
write(line) {
|
|
98
|
+
if (streamError || stream.destroyed) return;
|
|
99
|
+
stream.write(`${line}\n`);
|
|
100
|
+
},
|
|
101
|
+
close() {
|
|
102
|
+
if (streamError || stream.destroyed || stream.writableEnded) return Promise.resolve();
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
stream.end(() => {
|
|
105
|
+
resolve();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function createDebugChannel(options) {
|
|
112
|
+
if (!options.enabled) return {
|
|
113
|
+
enabled: false,
|
|
114
|
+
verbosity: options.verbosity ?? "compact",
|
|
115
|
+
emit() {},
|
|
116
|
+
close: NOOP_CLOSE
|
|
117
|
+
};
|
|
118
|
+
const verbosity = options.verbosity ?? "compact";
|
|
119
|
+
const sinks = [];
|
|
120
|
+
let reportPath;
|
|
121
|
+
if (options.report) {
|
|
122
|
+
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
123
|
+
const pid = options.pid ?? process.pid;
|
|
124
|
+
reportPath = resolveReportPath(options.report, now, pid);
|
|
125
|
+
sinks.push(createFileSink(reportPath));
|
|
126
|
+
if (options.report.tee) sinks.push(createTerminalSink());
|
|
127
|
+
} else sinks.push(createTerminalSink());
|
|
128
|
+
return {
|
|
129
|
+
enabled: true,
|
|
130
|
+
verbosity,
|
|
131
|
+
reportPath,
|
|
132
|
+
emit(event, details = {}, eventOptions = {}) {
|
|
133
|
+
if (!shouldEmitAtVerbosity(verbosity, eventOptions.verbosity ?? "compact")) return;
|
|
134
|
+
const payload = JSON.stringify({
|
|
135
|
+
event,
|
|
136
|
+
...details
|
|
137
|
+
});
|
|
138
|
+
for (const sink of sinks) sink.write(payload);
|
|
139
|
+
},
|
|
140
|
+
async close() {
|
|
141
|
+
for (const sink of sinks) await sink.close();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/cli/path/filter.ts
|
|
148
|
+
const DEFAULT_INCLUDE_EXTENSIONS = new Set([
|
|
149
|
+
".md",
|
|
150
|
+
".markdown",
|
|
151
|
+
".mdx",
|
|
152
|
+
".mdc",
|
|
153
|
+
".txt"
|
|
154
|
+
]);
|
|
155
|
+
function collectExtensionOption(value, previous = []) {
|
|
156
|
+
return [...previous, value];
|
|
157
|
+
}
|
|
158
|
+
function normalizeExtensionToken(value) {
|
|
159
|
+
const trimmed = value.trim().toLowerCase();
|
|
160
|
+
if (!trimmed) return null;
|
|
161
|
+
const normalized = trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
|
|
162
|
+
if (normalized === ".") return null;
|
|
163
|
+
return normalized;
|
|
164
|
+
}
|
|
165
|
+
function parseExtensionValues(values) {
|
|
166
|
+
const parsed = /* @__PURE__ */ new Set();
|
|
167
|
+
if (!values || values.length === 0) return parsed;
|
|
168
|
+
for (const value of values) for (const token of value.split(",")) {
|
|
169
|
+
const normalized = normalizeExtensionToken(token);
|
|
170
|
+
if (!normalized) continue;
|
|
171
|
+
parsed.add(normalized);
|
|
172
|
+
}
|
|
173
|
+
return parsed;
|
|
174
|
+
}
|
|
175
|
+
function buildDirectoryExtensionFilter(includeValues, excludeValues) {
|
|
176
|
+
const includeFromFlags = parseExtensionValues(includeValues);
|
|
177
|
+
const excludeExtensions = parseExtensionValues(excludeValues);
|
|
178
|
+
const includeExtensions = includeFromFlags.size > 0 ? includeFromFlags : new Set(DEFAULT_INCLUDE_EXTENSIONS);
|
|
179
|
+
const effectiveIncludeExtensions = /* @__PURE__ */ new Set();
|
|
180
|
+
for (const extension of includeExtensions) {
|
|
181
|
+
if (excludeExtensions.has(extension)) continue;
|
|
182
|
+
effectiveIncludeExtensions.add(extension);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
includeExtensions,
|
|
186
|
+
excludeExtensions,
|
|
187
|
+
effectiveIncludeExtensions
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function shouldIncludeFromDirectory(filePath, filter) {
|
|
191
|
+
const extension = extname(filePath).toLowerCase();
|
|
192
|
+
return filter.effectiveIncludeExtensions.has(extension);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/cli/total-of.ts
|
|
197
|
+
const TOTAL_OF_PARTS = [
|
|
198
|
+
"words",
|
|
199
|
+
"emoji",
|
|
200
|
+
"symbols",
|
|
201
|
+
"punctuation",
|
|
202
|
+
"whitespace"
|
|
203
|
+
];
|
|
204
|
+
const TOTAL_OF_PART_ALIASES = {
|
|
205
|
+
word: "words",
|
|
206
|
+
words: "words",
|
|
207
|
+
emoji: "emoji",
|
|
208
|
+
emojis: "emoji",
|
|
209
|
+
symbol: "symbols",
|
|
210
|
+
symbols: "symbols",
|
|
211
|
+
punction: "punctuation",
|
|
212
|
+
punctuation: "punctuation",
|
|
213
|
+
whitespace: "whitespace"
|
|
214
|
+
};
|
|
215
|
+
function createTotalOfCounts() {
|
|
216
|
+
return {
|
|
217
|
+
words: 0,
|
|
218
|
+
emoji: 0,
|
|
219
|
+
symbols: 0,
|
|
220
|
+
punctuation: 0,
|
|
221
|
+
whitespace: 0
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function collectNonWordCounts(target, nonWords) {
|
|
225
|
+
if (!nonWords) return;
|
|
226
|
+
target.emoji += nonWords.counts.emoji;
|
|
227
|
+
target.symbols += nonWords.counts.symbols;
|
|
228
|
+
target.punctuation += nonWords.counts.punctuation;
|
|
229
|
+
target.whitespace += nonWords.counts.whitespace ?? 0;
|
|
230
|
+
}
|
|
231
|
+
function collectFromWordCounterResult(result) {
|
|
232
|
+
const counts = createTotalOfCounts();
|
|
233
|
+
counts.words += result.counts?.words ?? result.total;
|
|
234
|
+
if (result.breakdown.mode === "collector") {
|
|
235
|
+
collectNonWordCounts(counts, result.breakdown.nonWords);
|
|
236
|
+
return counts;
|
|
237
|
+
}
|
|
238
|
+
for (const item of result.breakdown.items) collectNonWordCounts(counts, item.nonWords);
|
|
239
|
+
return counts;
|
|
240
|
+
}
|
|
241
|
+
function collectTotalOfCounts(result) {
|
|
242
|
+
if (!("section" in result)) return collectFromWordCounterResult(result);
|
|
243
|
+
const counts = createTotalOfCounts();
|
|
244
|
+
for (const item of result.items) {
|
|
245
|
+
const itemCounts = collectFromWordCounterResult(item.result);
|
|
246
|
+
for (const part of TOTAL_OF_PARTS) counts[part] += itemCounts[part];
|
|
247
|
+
}
|
|
248
|
+
return counts;
|
|
249
|
+
}
|
|
250
|
+
function parseTotalOfToken(token) {
|
|
251
|
+
const canonical = TOTAL_OF_PART_ALIASES[token.trim().toLowerCase()];
|
|
252
|
+
if (canonical) return canonical;
|
|
253
|
+
throw new Error(`Invalid --total-of part: ${token}. Allowed: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
254
|
+
}
|
|
255
|
+
function parseTotalOfOption(value) {
|
|
256
|
+
const rawTokens = value.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
257
|
+
if (rawTokens.length === 0) throw new Error(`Invalid --total-of value: "${value}". Use comma-separated parts from: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
258
|
+
const parts = [];
|
|
259
|
+
const seen = /* @__PURE__ */ new Set();
|
|
260
|
+
for (const token of rawTokens) {
|
|
261
|
+
const parsed = parseTotalOfToken(token);
|
|
262
|
+
if (seen.has(parsed)) continue;
|
|
263
|
+
seen.add(parsed);
|
|
264
|
+
parts.push(parsed);
|
|
265
|
+
}
|
|
266
|
+
return parts;
|
|
267
|
+
}
|
|
268
|
+
function requiresNonWordCollection(parts) {
|
|
269
|
+
if (!parts || parts.length === 0) return false;
|
|
270
|
+
return parts.some((part) => part !== "words");
|
|
271
|
+
}
|
|
272
|
+
function requiresWhitespaceCollection(parts) {
|
|
273
|
+
if (!parts || parts.length === 0) return false;
|
|
274
|
+
return parts.includes("whitespace");
|
|
275
|
+
}
|
|
276
|
+
function resolveTotalOfOverride(result, parts) {
|
|
277
|
+
if (!parts || parts.length === 0) return;
|
|
278
|
+
const counts = collectTotalOfCounts(result);
|
|
279
|
+
let total = 0;
|
|
280
|
+
for (const part of parts) total += counts[part];
|
|
281
|
+
return {
|
|
282
|
+
parts: [...parts],
|
|
283
|
+
total
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function formatTotalOfParts(parts) {
|
|
287
|
+
return parts.join(", ");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/cli/program/options.ts
|
|
292
|
+
const MODE_CHOICES = [
|
|
293
|
+
"chunk",
|
|
294
|
+
"segments",
|
|
295
|
+
"collector",
|
|
296
|
+
"char",
|
|
297
|
+
"char-collector"
|
|
298
|
+
];
|
|
299
|
+
const FORMAT_CHOICES = [
|
|
300
|
+
"standard",
|
|
301
|
+
"raw",
|
|
302
|
+
"json"
|
|
303
|
+
];
|
|
304
|
+
const SECTION_CHOICES = [
|
|
305
|
+
"all",
|
|
306
|
+
"split",
|
|
307
|
+
"frontmatter",
|
|
308
|
+
"content",
|
|
309
|
+
"per-key",
|
|
310
|
+
"split-per-key"
|
|
311
|
+
];
|
|
312
|
+
const PATH_MODE_CHOICES = ["auto", "manual"];
|
|
313
|
+
function collectPathValue(value, previous = []) {
|
|
314
|
+
return [...previous, value];
|
|
315
|
+
}
|
|
316
|
+
function configureProgramOptions(program, parseMode) {
|
|
317
|
+
program.addOption(new Option("-m, --mode <mode>", "breakdown mode").choices(MODE_CHOICES).argParser(parseMode).default("chunk")).addOption(new Option("-f, --format <format>", "output format").choices(FORMAT_CHOICES).default("standard")).addOption(new Option("--section <section>", "document section mode").choices(SECTION_CHOICES).default("all")).addOption(new Option("--path-mode <mode>", "path resolution mode").choices(PATH_MODE_CHOICES).default("auto")).option("--latin-language <language>", "hint a language tag for Latin script text").option("--latin-tag <tag>", "hint a BCP 47 tag for Latin script text").option("--latin-locale <locale>", "legacy alias of --latin-language").option("--han-language <language>", "hint a language tag for Han script text").option("--han-tag <tag>", "hint a BCP 47 tag for Han script text").option("--non-words", "collect emoji, symbols, and punctuation (excludes whitespace)").option("--include-whitespace", "include whitespace counts (implies with --non-words; same as --misc)").option("--misc", "collect non-words plus whitespace (alias for --include-whitespace)").option("--total-of <parts>", "override total composition (comma-separated): words,emoji,symbols,punctuation,whitespace", parseTotalOfOption).option("--pretty", "pretty print JSON output", false).option("--debug", "enable debug diagnostics on stderr").option("--verbose", "emit verbose per-file debug diagnostics (requires --debug)").option("--debug-report [path]", "write debug diagnostics to a report file").option("--debug-report-tee", "mirror debug diagnostics to both report file and stderr").option("--debug-tee", "alias of --debug-report-tee").option("--merged", "show merged aggregate output (default)").option("--per-file", "show per-file output plus merged summary").option("--no-progress", "disable batch progress indicator").option("--keep-progress", "keep final batch progress line visible in standard mode").option("--no-recursive", "disable recursive directory traversal").option("--quiet-skips", "hide skip diagnostics (applies when --debug is enabled)").option("--include-ext <exts>", "comma-separated extensions to include during directory scanning", collectExtensionOption, []).option("--exclude-ext <exts>", "comma-separated extensions to exclude during directory scanning", collectExtensionOption, []).option("-p, --path <path>", "read input from file or directory", collectPathValue, []).argument("[text...]", "text to count").showHelpAfterError();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region node_modules/picocolors/picocolors.js
|
|
322
|
+
var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
323
|
+
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
324
|
+
let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
325
|
+
let formatter = (open, close, replace = open) => (input) => {
|
|
326
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
327
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
328
|
+
};
|
|
329
|
+
let replaceClose = (string, close, replace, index) => {
|
|
330
|
+
let result = "", cursor = 0;
|
|
331
|
+
do {
|
|
332
|
+
result += string.substring(cursor, index) + replace;
|
|
333
|
+
cursor = index + close.length;
|
|
334
|
+
index = string.indexOf(close, cursor);
|
|
335
|
+
} while (~index);
|
|
336
|
+
return result + string.substring(cursor);
|
|
337
|
+
};
|
|
338
|
+
let createColors = (enabled = isColorSupported) => {
|
|
339
|
+
let f = enabled ? formatter : () => String;
|
|
340
|
+
return {
|
|
341
|
+
isColorSupported: enabled,
|
|
342
|
+
reset: f("\x1B[0m", "\x1B[0m"),
|
|
343
|
+
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
344
|
+
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
345
|
+
italic: f("\x1B[3m", "\x1B[23m"),
|
|
346
|
+
underline: f("\x1B[4m", "\x1B[24m"),
|
|
347
|
+
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
348
|
+
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
349
|
+
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
350
|
+
black: f("\x1B[30m", "\x1B[39m"),
|
|
351
|
+
red: f("\x1B[31m", "\x1B[39m"),
|
|
352
|
+
green: f("\x1B[32m", "\x1B[39m"),
|
|
353
|
+
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
354
|
+
blue: f("\x1B[34m", "\x1B[39m"),
|
|
355
|
+
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
356
|
+
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
357
|
+
white: f("\x1B[37m", "\x1B[39m"),
|
|
358
|
+
gray: f("\x1B[90m", "\x1B[39m"),
|
|
359
|
+
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
360
|
+
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
361
|
+
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
362
|
+
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
363
|
+
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
364
|
+
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
365
|
+
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
366
|
+
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
367
|
+
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
368
|
+
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
369
|
+
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
370
|
+
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
371
|
+
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
372
|
+
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
373
|
+
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
374
|
+
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
375
|
+
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
376
|
+
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
377
|
+
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
378
|
+
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
379
|
+
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
380
|
+
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
381
|
+
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
382
|
+
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
module.exports = createColors();
|
|
386
|
+
module.exports.createColors = createColors;
|
|
387
|
+
}));
|
|
388
|
+
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/cli/program/version.ts
|
|
391
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
392
|
+
function* candidateSearchRoots() {
|
|
393
|
+
yield dirname(fileURLToPath(import.meta.url));
|
|
394
|
+
const argvPath = process.argv[1];
|
|
395
|
+
if (typeof argvPath === "string" && argvPath.length > 0) yield dirname(resolve(argvPath));
|
|
396
|
+
yield process.cwd();
|
|
397
|
+
}
|
|
398
|
+
function* walkUpDirectories(start, maxLevels) {
|
|
399
|
+
let current = start;
|
|
400
|
+
for (let depth = 0; depth < maxLevels; depth += 1) {
|
|
401
|
+
yield current;
|
|
402
|
+
const parent = dirname(current);
|
|
403
|
+
if (parent === current) break;
|
|
404
|
+
current = parent;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function resolveVersionFromPath(start, maxLevels) {
|
|
408
|
+
for (const directory of walkUpDirectories(start, maxLevels)) try {
|
|
409
|
+
const raw = readFileSync(join(directory, "package.json"), "utf8");
|
|
410
|
+
const data = JSON.parse(raw);
|
|
411
|
+
if (data.version) return data.version;
|
|
412
|
+
} catch {}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
function resolvePackageVersion() {
|
|
416
|
+
const maxLevels = 8;
|
|
417
|
+
const seen = /* @__PURE__ */ new Set();
|
|
418
|
+
for (const root of candidateSearchRoots()) {
|
|
419
|
+
if (seen.has(root)) continue;
|
|
420
|
+
seen.add(root);
|
|
421
|
+
const version = resolveVersionFromPath(root, maxLevels);
|
|
422
|
+
if (version) return version;
|
|
423
|
+
}
|
|
424
|
+
return "0.0.0";
|
|
425
|
+
}
|
|
426
|
+
function getFormattedVersionLabel() {
|
|
427
|
+
const version = resolvePackageVersion();
|
|
428
|
+
return import_picocolors.default.bgBlack(import_picocolors.default.bold(import_picocolors.default.italic(` word-counter ${import_picocolors.default.cyanBright(`ver.${version}`)} `)));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region src/utils/append-all.ts
|
|
433
|
+
function appendAll(target, source) {
|
|
434
|
+
for (const item of source) target.push(item);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/cli/path/load.ts
|
|
439
|
+
function isProbablyBinary(buffer) {
|
|
440
|
+
if (buffer.length === 0) return false;
|
|
441
|
+
const sampleSize = Math.min(buffer.length, 1024);
|
|
442
|
+
let suspicious = 0;
|
|
443
|
+
for (let index = 0; index < sampleSize; index += 1) {
|
|
444
|
+
const byte = buffer[index] ?? 0;
|
|
445
|
+
if (byte === 0) return true;
|
|
446
|
+
if (byte === 9 || byte === 10 || byte === 13) continue;
|
|
447
|
+
if (byte >= 32 && byte <= 126) continue;
|
|
448
|
+
if (byte >= 128) continue;
|
|
449
|
+
suspicious += 1;
|
|
450
|
+
}
|
|
451
|
+
return suspicious / sampleSize > .3;
|
|
452
|
+
}
|
|
453
|
+
async function loadBatchInputs(filePaths) {
|
|
454
|
+
const files = [];
|
|
455
|
+
const skipped = [];
|
|
456
|
+
for (const filePath of filePaths) {
|
|
457
|
+
let buffer;
|
|
458
|
+
try {
|
|
459
|
+
buffer = await readFile(filePath);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
462
|
+
skipped.push({
|
|
463
|
+
path: filePath,
|
|
464
|
+
reason: `not readable: ${message}`
|
|
465
|
+
});
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (isProbablyBinary(buffer)) {
|
|
469
|
+
skipped.push({
|
|
470
|
+
path: filePath,
|
|
471
|
+
reason: "binary file"
|
|
472
|
+
});
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
files.push({
|
|
476
|
+
path: filePath,
|
|
477
|
+
content: buffer.toString("utf8")
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
return {
|
|
481
|
+
files,
|
|
482
|
+
skipped
|
|
483
|
+
};
|
|
484
|
+
}
|
|
8
485
|
|
|
9
|
-
//#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
486
|
+
//#endregion
|
|
487
|
+
//#region src/cli/path/resolve.ts
|
|
488
|
+
async function expandDirectory(directoryPath, recursive, filter, skipped, debug, stats) {
|
|
489
|
+
let entries;
|
|
490
|
+
try {
|
|
491
|
+
entries = await readdir(directoryPath, {
|
|
492
|
+
withFileTypes: true,
|
|
493
|
+
encoding: "utf8"
|
|
494
|
+
});
|
|
495
|
+
} catch (error) {
|
|
496
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
497
|
+
skipped.push({
|
|
498
|
+
path: directoryPath,
|
|
499
|
+
reason: `directory read failed: ${message}`
|
|
500
|
+
});
|
|
501
|
+
debug.emit("path.resolve.expand.read_failed", {
|
|
502
|
+
directory: directoryPath,
|
|
503
|
+
reason: `directory read failed: ${message}`
|
|
504
|
+
});
|
|
505
|
+
return [];
|
|
506
|
+
}
|
|
507
|
+
const sortedEntries = entries.slice().sort((left, right) => left.name.localeCompare(right.name));
|
|
508
|
+
const files = [];
|
|
509
|
+
debug.emit("path.resolve.expand.start", {
|
|
510
|
+
directory: directoryPath,
|
|
511
|
+
entries: sortedEntries.length,
|
|
512
|
+
recursive
|
|
513
|
+
});
|
|
514
|
+
for (const entry of sortedEntries) {
|
|
515
|
+
const entryPath = resolve(directoryPath, entry.name);
|
|
516
|
+
if (entry.isFile()) {
|
|
517
|
+
if (!shouldIncludeFromDirectory(entryPath, filter)) {
|
|
518
|
+
skipped.push({
|
|
519
|
+
path: entryPath,
|
|
520
|
+
reason: "extension excluded"
|
|
25
521
|
});
|
|
522
|
+
debug.emit("path.resolve.filter.excluded", {
|
|
523
|
+
path: entryPath,
|
|
524
|
+
reason: "extension excluded"
|
|
525
|
+
}, { verbosity: "verbose" });
|
|
526
|
+
stats.filterExcluded += 1;
|
|
527
|
+
continue;
|
|
26
528
|
}
|
|
529
|
+
files.push(entryPath);
|
|
530
|
+
stats.directoryIncluded += 1;
|
|
531
|
+
debug.emit("path.resolve.expand.include", {
|
|
532
|
+
path: entryPath,
|
|
533
|
+
source: "directory"
|
|
534
|
+
}, { verbosity: "verbose" });
|
|
535
|
+
continue;
|
|
27
536
|
}
|
|
537
|
+
if (!entry.isDirectory() || !recursive) continue;
|
|
538
|
+
appendAll(files, await expandDirectory(entryPath, recursive, filter, skipped, debug, stats));
|
|
28
539
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
540
|
+
debug.emit("path.resolve.expand.complete", {
|
|
541
|
+
directory: directoryPath,
|
|
542
|
+
files: files.length
|
|
543
|
+
});
|
|
544
|
+
return files;
|
|
545
|
+
}
|
|
546
|
+
async function resolveBatchFilePaths(pathInputs, options) {
|
|
547
|
+
const skipped = [];
|
|
548
|
+
const resolvedFiles = /* @__PURE__ */ new Set();
|
|
549
|
+
const stats = {
|
|
550
|
+
dedupeAccepted: 0,
|
|
551
|
+
dedupeDuplicates: 0,
|
|
552
|
+
filterExcluded: 0,
|
|
553
|
+
directoryIncluded: 0
|
|
554
|
+
};
|
|
555
|
+
const extensionFilter = options.extensionFilter ?? buildDirectoryExtensionFilter(void 0, void 0);
|
|
556
|
+
const debug = options.debug ?? {
|
|
557
|
+
enabled: false,
|
|
558
|
+
verbosity: "compact",
|
|
559
|
+
emit() {},
|
|
560
|
+
close: async () => {}
|
|
561
|
+
};
|
|
562
|
+
debug.emit("path.resolve.inputs", {
|
|
563
|
+
inputs: pathInputs.length,
|
|
564
|
+
pathMode: options.pathMode,
|
|
565
|
+
recursive: options.recursive
|
|
566
|
+
});
|
|
567
|
+
const addResolvedFile = (filePath, details) => {
|
|
568
|
+
if (resolvedFiles.has(filePath)) {
|
|
569
|
+
stats.dedupeDuplicates += 1;
|
|
570
|
+
debug.emit("path.resolve.dedupe.duplicate", {
|
|
571
|
+
path: filePath,
|
|
572
|
+
source: details.source,
|
|
573
|
+
input: details.input
|
|
574
|
+
}, { verbosity: "verbose" });
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
resolvedFiles.add(filePath);
|
|
578
|
+
stats.dedupeAccepted += 1;
|
|
579
|
+
debug.emit("path.resolve.dedupe.accept", {
|
|
580
|
+
path: filePath,
|
|
581
|
+
source: details.source,
|
|
582
|
+
input: details.input
|
|
583
|
+
}, { verbosity: "verbose" });
|
|
584
|
+
};
|
|
585
|
+
for (const rawPath of pathInputs) {
|
|
586
|
+
const targetPath = resolve(rawPath);
|
|
587
|
+
debug.emit("path.resolve.input", {
|
|
588
|
+
rawPath,
|
|
589
|
+
resolvedPath: targetPath
|
|
590
|
+
});
|
|
591
|
+
let metadata;
|
|
592
|
+
try {
|
|
593
|
+
metadata = await stat(targetPath);
|
|
594
|
+
} catch (error) {
|
|
595
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
596
|
+
skipped.push({
|
|
597
|
+
path: targetPath,
|
|
598
|
+
reason: `not readable: ${message}`
|
|
599
|
+
});
|
|
600
|
+
debug.emit("path.resolve.skip", {
|
|
601
|
+
path: targetPath,
|
|
602
|
+
reason: `not readable: ${message}`
|
|
603
|
+
});
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (metadata.isDirectory() && options.pathMode === "auto") {
|
|
607
|
+
debug.emit("path.resolve.root.expand", {
|
|
608
|
+
root: targetPath,
|
|
609
|
+
recursive: options.recursive
|
|
610
|
+
});
|
|
611
|
+
const files = await expandDirectory(targetPath, options.recursive, extensionFilter, skipped, debug, stats);
|
|
612
|
+
for (const file of files) addResolvedFile(file, {
|
|
613
|
+
source: "directory",
|
|
614
|
+
input: targetPath
|
|
615
|
+
});
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
if (!metadata.isFile()) {
|
|
619
|
+
skipped.push({
|
|
620
|
+
path: targetPath,
|
|
621
|
+
reason: "not a regular file"
|
|
622
|
+
});
|
|
623
|
+
debug.emit("path.resolve.skip", {
|
|
624
|
+
path: targetPath,
|
|
625
|
+
reason: "not a regular file"
|
|
626
|
+
});
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
addResolvedFile(targetPath, {
|
|
630
|
+
source: "direct",
|
|
631
|
+
input: targetPath
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
const files = [...resolvedFiles].sort((left, right) => left.localeCompare(right));
|
|
635
|
+
debug.emit("path.resolve.filter.summary", {
|
|
636
|
+
excluded: stats.filterExcluded,
|
|
637
|
+
included: stats.directoryIncluded
|
|
638
|
+
});
|
|
639
|
+
debug.emit("path.resolve.dedupe.summary", {
|
|
640
|
+
accepted: stats.dedupeAccepted,
|
|
641
|
+
duplicates: stats.dedupeDuplicates
|
|
642
|
+
});
|
|
643
|
+
debug.emit("path.resolve.complete", {
|
|
644
|
+
files: files.length,
|
|
645
|
+
skipped: skipped.length,
|
|
646
|
+
ordering: "absolute-path-ascending"
|
|
647
|
+
});
|
|
648
|
+
return {
|
|
649
|
+
files,
|
|
650
|
+
skipped
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/cli/progress/reporter.ts
|
|
656
|
+
const PROGRESS_BAR_WIDTH = 20;
|
|
657
|
+
const FILLED_BAR_CHAR = "█";
|
|
658
|
+
const EMPTY_BAR_CHAR = "░";
|
|
659
|
+
function clamp(value, min, max) {
|
|
660
|
+
return Math.max(min, Math.min(max, value));
|
|
661
|
+
}
|
|
662
|
+
function buildProgressBar(completed, total) {
|
|
663
|
+
const safeTotal = Math.max(total, 1);
|
|
664
|
+
const ratio = clamp(completed / safeTotal, 0, 1);
|
|
665
|
+
const filled = completed >= safeTotal ? PROGRESS_BAR_WIDTH : Math.floor(ratio * PROGRESS_BAR_WIDTH);
|
|
666
|
+
const empty = PROGRESS_BAR_WIDTH - filled;
|
|
667
|
+
return `${FILLED_BAR_CHAR.repeat(filled)}${EMPTY_BAR_CHAR.repeat(empty)}`;
|
|
668
|
+
}
|
|
669
|
+
function formatElapsed(startedAtMs) {
|
|
670
|
+
const elapsedMs = Date.now() - startedAtMs;
|
|
671
|
+
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
672
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
673
|
+
const seconds = totalSeconds % 60;
|
|
674
|
+
const tenths = Math.floor(Math.max(0, elapsedMs) % 1e3 / 100);
|
|
675
|
+
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}.${tenths}`;
|
|
676
|
+
}
|
|
677
|
+
function buildProgressLine(completed, total, startedAtMs) {
|
|
678
|
+
const safeTotal = Math.max(total, 1);
|
|
679
|
+
const percent = completed >= safeTotal ? 100 : Math.floor(completed / safeTotal * 100);
|
|
680
|
+
return `Counting files [${buildProgressBar(completed, safeTotal)}] ${`${String(percent).padStart(3, " ")}%`} ${String(completed).padStart(String(safeTotal).length, " ")}/${safeTotal} elapsed ${formatElapsed(startedAtMs)}`;
|
|
681
|
+
}
|
|
682
|
+
function buildFinalizingLine(startedAtMs) {
|
|
683
|
+
return `Finalizing aggregate... elapsed ${formatElapsed(startedAtMs)}`;
|
|
684
|
+
}
|
|
685
|
+
function createBatchProgressReporter(options) {
|
|
686
|
+
const enabled = options.enabled;
|
|
687
|
+
const isTTY = Boolean(options.stream.isTTY);
|
|
688
|
+
const clearOnFinish = options.clearOnFinish ?? true;
|
|
689
|
+
let active = false;
|
|
690
|
+
let total = 0;
|
|
691
|
+
let lastLineLength = 0;
|
|
692
|
+
let startedAtMs = 0;
|
|
693
|
+
let lastRenderedPercent = -1;
|
|
694
|
+
let finalizingStarted = false;
|
|
695
|
+
const writeTTYLine = (line) => {
|
|
696
|
+
const trailingPadding = lastLineLength > line.length ? " ".repeat(lastLineLength - line.length) : "";
|
|
697
|
+
options.stream.write(`\r${line}${trailingPadding}`);
|
|
698
|
+
lastLineLength = line.length;
|
|
699
|
+
};
|
|
700
|
+
const render = (completed) => {
|
|
701
|
+
const line = buildProgressLine(completed, total, startedAtMs);
|
|
702
|
+
const safeTotal = Math.max(total, 1);
|
|
703
|
+
const percent = completed >= safeTotal ? 100 : Math.floor(completed / safeTotal * 100);
|
|
704
|
+
if (!isTTY && percent === lastRenderedPercent && completed < safeTotal) return;
|
|
705
|
+
lastRenderedPercent = percent;
|
|
706
|
+
if (isTTY) {
|
|
707
|
+
writeTTYLine(line);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
lastLineLength = line.length;
|
|
711
|
+
options.stream.write(`${line}\n`);
|
|
712
|
+
};
|
|
713
|
+
const clearLine = () => {
|
|
714
|
+
if (lastLineLength === 0) return;
|
|
715
|
+
options.stream.write(`\r${" ".repeat(lastLineLength)}\r`);
|
|
716
|
+
lastLineLength = 0;
|
|
717
|
+
};
|
|
718
|
+
return {
|
|
719
|
+
enabled,
|
|
720
|
+
start(nextTotal, nextStartedAtMs) {
|
|
721
|
+
if (!enabled || nextTotal <= 1) return;
|
|
722
|
+
total = nextTotal;
|
|
723
|
+
active = true;
|
|
724
|
+
startedAtMs = nextStartedAtMs ?? Date.now();
|
|
725
|
+
lastRenderedPercent = -1;
|
|
726
|
+
finalizingStarted = false;
|
|
727
|
+
render(0);
|
|
728
|
+
},
|
|
729
|
+
advance(snapshot) {
|
|
730
|
+
if (!active) return;
|
|
731
|
+
render(snapshot.completed);
|
|
732
|
+
},
|
|
733
|
+
startFinalizing() {
|
|
734
|
+
if (!active || finalizingStarted) return;
|
|
735
|
+
finalizingStarted = true;
|
|
736
|
+
const line = buildFinalizingLine(startedAtMs);
|
|
737
|
+
if (isTTY) {
|
|
738
|
+
if (!clearOnFinish) {
|
|
739
|
+
options.stream.write(`\n${line}`);
|
|
740
|
+
lastLineLength = line.length;
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
writeTTYLine(line);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
lastLineLength = line.length;
|
|
747
|
+
options.stream.write(`${line}\n`);
|
|
748
|
+
},
|
|
749
|
+
finish() {
|
|
750
|
+
if (!active) return;
|
|
751
|
+
if (isTTY) if (clearOnFinish) clearLine();
|
|
752
|
+
else options.stream.write("\n");
|
|
753
|
+
active = false;
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
35
757
|
|
|
36
758
|
//#endregion
|
|
37
759
|
//#region src/markdown/toml/arrays.ts
|
|
@@ -329,7 +1051,7 @@ function parseTomlFrontmatter(frontmatter) {
|
|
|
329
1051
|
index += 1;
|
|
330
1052
|
const nextLine = lines[index] ?? "";
|
|
331
1053
|
combined += `\n${nextLine}`;
|
|
332
|
-
if (
|
|
1054
|
+
if (new RegExp(`${delimiter}\\s*$`).test(nextLine)) {
|
|
333
1055
|
closed = true;
|
|
334
1056
|
break;
|
|
335
1057
|
}
|
|
@@ -457,10 +1179,10 @@ function parseMarkdown(input) {
|
|
|
457
1179
|
data: null,
|
|
458
1180
|
frontmatterType: null
|
|
459
1181
|
};
|
|
460
|
-
const frontmatter
|
|
1182
|
+
const frontmatter = jsonBlock.jsonText;
|
|
461
1183
|
let content = normalizedWithoutBom.slice(jsonBlock.endIndex + 1);
|
|
462
1184
|
if (content.startsWith("\n")) content = content.slice(1);
|
|
463
|
-
const data = parseFrontmatter(frontmatter
|
|
1185
|
+
const data = parseFrontmatter(frontmatter, "json");
|
|
464
1186
|
if (!data) return {
|
|
465
1187
|
frontmatter: null,
|
|
466
1188
|
content: normalizedWithoutBom,
|
|
@@ -468,7 +1190,7 @@ function parseMarkdown(input) {
|
|
|
468
1190
|
frontmatterType: null
|
|
469
1191
|
};
|
|
470
1192
|
return {
|
|
471
|
-
frontmatter
|
|
1193
|
+
frontmatter,
|
|
472
1194
|
content,
|
|
473
1195
|
data,
|
|
474
1196
|
frontmatterType: "json"
|
|
@@ -523,12 +1245,6 @@ function countCharsForLocale(text, locale) {
|
|
|
523
1245
|
return count;
|
|
524
1246
|
}
|
|
525
1247
|
|
|
526
|
-
//#endregion
|
|
527
|
-
//#region src/utils/append-all.ts
|
|
528
|
-
function appendAll(target, source) {
|
|
529
|
-
for (const item of source) target.push(item);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
1248
|
//#endregion
|
|
533
1249
|
//#region src/wc/non-words.ts
|
|
534
1250
|
const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation})/u;
|
|
@@ -697,6 +1413,32 @@ function analyzeCharChunk(chunk, collectNonWords, includeWhitespace) {
|
|
|
697
1413
|
nonWords: nonWords ?? void 0
|
|
698
1414
|
};
|
|
699
1415
|
}
|
|
1416
|
+
function aggregateCharsByLocale(chunks) {
|
|
1417
|
+
const order = [];
|
|
1418
|
+
const map = /* @__PURE__ */ new Map();
|
|
1419
|
+
for (const chunk of chunks) {
|
|
1420
|
+
const existing = map.get(chunk.locale);
|
|
1421
|
+
if (existing) {
|
|
1422
|
+
existing.chars += chunk.chars;
|
|
1423
|
+
existing.wordChars += chunk.wordChars;
|
|
1424
|
+
existing.nonWordChars += chunk.nonWordChars;
|
|
1425
|
+
if (chunk.nonWords) {
|
|
1426
|
+
if (!existing.nonWords) existing.nonWords = createNonWordCollection();
|
|
1427
|
+
mergeNonWordCollections(existing.nonWords, chunk.nonWords);
|
|
1428
|
+
}
|
|
1429
|
+
continue;
|
|
1430
|
+
}
|
|
1431
|
+
order.push(chunk.locale);
|
|
1432
|
+
map.set(chunk.locale, {
|
|
1433
|
+
locale: chunk.locale,
|
|
1434
|
+
chars: chunk.chars,
|
|
1435
|
+
wordChars: chunk.wordChars,
|
|
1436
|
+
nonWordChars: chunk.nonWordChars,
|
|
1437
|
+
nonWords: chunk.nonWords ? mergeNonWordCollections(createNonWordCollection(), chunk.nonWords) : void 0
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
return order.map((locale) => map.get(locale));
|
|
1441
|
+
}
|
|
700
1442
|
function aggregateByLocale(chunks) {
|
|
701
1443
|
const order = [];
|
|
702
1444
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -731,11 +1473,55 @@ const MODE_ALIASES = {
|
|
|
731
1473
|
char: "char",
|
|
732
1474
|
chars: "char",
|
|
733
1475
|
character: "char",
|
|
734
|
-
characters: "char"
|
|
1476
|
+
characters: "char",
|
|
1477
|
+
"char-collector": "char-collector"
|
|
735
1478
|
};
|
|
1479
|
+
const CHAR_MODE_ALIASES = new Set([
|
|
1480
|
+
"char",
|
|
1481
|
+
"chars",
|
|
1482
|
+
"character",
|
|
1483
|
+
"characters"
|
|
1484
|
+
]);
|
|
1485
|
+
const COLLECTOR_MODE_ALIASES = new Set([
|
|
1486
|
+
"collector",
|
|
1487
|
+
"collect",
|
|
1488
|
+
"colle",
|
|
1489
|
+
"col"
|
|
1490
|
+
]);
|
|
1491
|
+
function collapseSeparators(value) {
|
|
1492
|
+
return value.replace(/[-_\s]+/g, "");
|
|
1493
|
+
}
|
|
1494
|
+
function isComposedCharCollectorFromTokens(value) {
|
|
1495
|
+
const tokens = value.split(/[-_\s]+/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
1496
|
+
if (tokens.length < 2) return false;
|
|
1497
|
+
let hasCharAlias = false;
|
|
1498
|
+
let hasCollectorAlias = false;
|
|
1499
|
+
for (const token of tokens) {
|
|
1500
|
+
if (CHAR_MODE_ALIASES.has(token)) {
|
|
1501
|
+
hasCharAlias = true;
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1504
|
+
if (COLLECTOR_MODE_ALIASES.has(token)) {
|
|
1505
|
+
hasCollectorAlias = true;
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
return false;
|
|
1509
|
+
}
|
|
1510
|
+
return hasCharAlias && hasCollectorAlias;
|
|
1511
|
+
}
|
|
1512
|
+
function isComposedCharCollectorCompact(value) {
|
|
1513
|
+
for (const charAlias of CHAR_MODE_ALIASES) for (const collectorAlias of COLLECTOR_MODE_ALIASES) if (value === `${charAlias}${collectorAlias}` || value === `${collectorAlias}${charAlias}`) return true;
|
|
1514
|
+
return false;
|
|
1515
|
+
}
|
|
736
1516
|
function normalizeMode(input) {
|
|
737
1517
|
if (!input) return null;
|
|
738
|
-
|
|
1518
|
+
const normalized = input.trim().toLowerCase();
|
|
1519
|
+
const direct = MODE_ALIASES[normalized];
|
|
1520
|
+
if (direct) return direct;
|
|
1521
|
+
if (isComposedCharCollectorFromTokens(normalized)) return "char-collector";
|
|
1522
|
+
const compact = collapseSeparators(normalized);
|
|
1523
|
+
if (isComposedCharCollectorCompact(compact)) return "char-collector";
|
|
1524
|
+
return MODE_ALIASES[compact] ?? null;
|
|
739
1525
|
}
|
|
740
1526
|
function resolveMode(input, fallback = "chunk") {
|
|
741
1527
|
return normalizeMode(input) ?? fallback;
|
|
@@ -897,25 +1683,37 @@ function wordCounter(text, options = {}) {
|
|
|
897
1683
|
hanLanguageHint: options.hanLanguageHint,
|
|
898
1684
|
hanTagHint: options.hanTagHint
|
|
899
1685
|
});
|
|
900
|
-
if (mode === "char") {
|
|
901
|
-
const analyzed
|
|
902
|
-
const total
|
|
903
|
-
const
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1686
|
+
if (mode === "char" || mode === "char-collector") {
|
|
1687
|
+
const analyzed = chunks.map((chunk) => analyzeCharChunk(chunk, collectNonWords, includeWhitespace));
|
|
1688
|
+
const total = analyzed.reduce((sum, chunk) => sum + chunk.chars, 0);
|
|
1689
|
+
const counts = collectNonWords ? {
|
|
1690
|
+
words: analyzed.reduce((sum, chunk) => sum + chunk.wordChars, 0),
|
|
1691
|
+
nonWords: analyzed.reduce((sum, chunk) => sum + chunk.nonWordChars, 0),
|
|
1692
|
+
total
|
|
1693
|
+
} : void 0;
|
|
1694
|
+
if (mode === "char") return {
|
|
1695
|
+
total,
|
|
1696
|
+
counts,
|
|
1697
|
+
breakdown: {
|
|
1698
|
+
mode,
|
|
1699
|
+
items: analyzed.map((chunk) => ({
|
|
1700
|
+
locale: chunk.locale,
|
|
1701
|
+
text: chunk.text,
|
|
1702
|
+
chars: chunk.chars,
|
|
1703
|
+
nonWords: chunk.nonWords
|
|
1704
|
+
}))
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
909
1707
|
return {
|
|
910
|
-
total
|
|
911
|
-
counts
|
|
912
|
-
words: analyzed$1.reduce((sum, chunk) => sum + chunk.wordChars, 0),
|
|
913
|
-
nonWords: analyzed$1.reduce((sum, chunk) => sum + chunk.nonWordChars, 0),
|
|
914
|
-
total: total$1
|
|
915
|
-
} : void 0,
|
|
1708
|
+
total,
|
|
1709
|
+
counts,
|
|
916
1710
|
breakdown: {
|
|
917
1711
|
mode,
|
|
918
|
-
items
|
|
1712
|
+
items: aggregateCharsByLocale(analyzed).map((chunk) => ({
|
|
1713
|
+
locale: chunk.locale,
|
|
1714
|
+
chars: chunk.chars,
|
|
1715
|
+
nonWords: chunk.nonWords
|
|
1716
|
+
}))
|
|
919
1717
|
}
|
|
920
1718
|
};
|
|
921
1719
|
}
|
|
@@ -1006,327 +1804,51 @@ function buildPerKeyItems(data, mode, options) {
|
|
|
1006
1804
|
return Object.entries(data).map(([key, value]) => {
|
|
1007
1805
|
const valueText = normalizeText(value);
|
|
1008
1806
|
return {
|
|
1009
|
-
name: key,
|
|
1010
|
-
source: "frontmatter",
|
|
1011
|
-
result: wc_default(valueText ? `${key}: ${valueText}` : key, options)
|
|
1012
|
-
};
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
function buildSingleItem(name, text, mode, options, source) {
|
|
1016
|
-
return [{
|
|
1017
|
-
name,
|
|
1018
|
-
source,
|
|
1019
|
-
result: wc_default(text, options)
|
|
1020
|
-
}];
|
|
1021
|
-
}
|
|
1022
|
-
function sumTotals(items) {
|
|
1023
|
-
return items.reduce((sum, item) => sum + item.result.total, 0);
|
|
1024
|
-
}
|
|
1025
|
-
function countSections(input, section, options = {}) {
|
|
1026
|
-
const mode = options.mode ?? "chunk";
|
|
1027
|
-
if (section === "all") {
|
|
1028
|
-
const result = wc_default(input, options);
|
|
1029
|
-
return {
|
|
1030
|
-
section,
|
|
1031
|
-
total: result.total,
|
|
1032
|
-
frontmatterType: null,
|
|
1033
|
-
items: [{
|
|
1034
|
-
name: "all",
|
|
1035
|
-
source: "content",
|
|
1036
|
-
result
|
|
1037
|
-
}]
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
const parsed = parseMarkdown(input);
|
|
1041
|
-
const frontmatterText = parsed.frontmatter ?? "";
|
|
1042
|
-
const contentText = parsed.content ?? "";
|
|
1043
|
-
let items = [];
|
|
1044
|
-
if (section === "frontmatter") items = buildSingleItem("frontmatter", frontmatterText, mode, options, "frontmatter");
|
|
1045
|
-
else if (section === "content") items = buildSingleItem("content", contentText, mode, options, "content");
|
|
1046
|
-
else if (section === "split") items = [...buildSingleItem("frontmatter", frontmatterText, mode, options, "frontmatter"), ...buildSingleItem("content", contentText, mode, options, "content")];
|
|
1047
|
-
else if (section === "per-key") items = buildPerKeyItems(parsed.data, mode, options);
|
|
1048
|
-
else if (section === "split-per-key") items = [...buildPerKeyItems(parsed.data, mode, options), ...buildSingleItem("content", contentText, mode, options, "content")];
|
|
1049
|
-
return {
|
|
1050
|
-
section,
|
|
1051
|
-
total: sumTotals(items),
|
|
1052
|
-
frontmatterType: parsed.frontmatterType,
|
|
1053
|
-
items
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
//#endregion
|
|
1058
|
-
//#region src/cli/path/filter.ts
|
|
1059
|
-
const DEFAULT_INCLUDE_EXTENSIONS = new Set([
|
|
1060
|
-
".md",
|
|
1061
|
-
".markdown",
|
|
1062
|
-
".mdx",
|
|
1063
|
-
".mdc",
|
|
1064
|
-
".txt"
|
|
1065
|
-
]);
|
|
1066
|
-
function collectExtensionOption(value, previous = []) {
|
|
1067
|
-
return [...previous, value];
|
|
1068
|
-
}
|
|
1069
|
-
function normalizeExtensionToken(value) {
|
|
1070
|
-
const trimmed = value.trim().toLowerCase();
|
|
1071
|
-
if (!trimmed) return null;
|
|
1072
|
-
const normalized = trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
|
|
1073
|
-
if (normalized === ".") return null;
|
|
1074
|
-
return normalized;
|
|
1075
|
-
}
|
|
1076
|
-
function parseExtensionValues(values) {
|
|
1077
|
-
const parsed = /* @__PURE__ */ new Set();
|
|
1078
|
-
if (!values || values.length === 0) return parsed;
|
|
1079
|
-
for (const value of values) for (const token of value.split(",")) {
|
|
1080
|
-
const normalized = normalizeExtensionToken(token);
|
|
1081
|
-
if (!normalized) continue;
|
|
1082
|
-
parsed.add(normalized);
|
|
1083
|
-
}
|
|
1084
|
-
return parsed;
|
|
1085
|
-
}
|
|
1086
|
-
function buildDirectoryExtensionFilter(includeValues, excludeValues) {
|
|
1087
|
-
const includeFromFlags = parseExtensionValues(includeValues);
|
|
1088
|
-
const excludeExtensions = parseExtensionValues(excludeValues);
|
|
1089
|
-
const includeExtensions = includeFromFlags.size > 0 ? includeFromFlags : new Set(DEFAULT_INCLUDE_EXTENSIONS);
|
|
1090
|
-
const effectiveIncludeExtensions = /* @__PURE__ */ new Set();
|
|
1091
|
-
for (const extension of includeExtensions) {
|
|
1092
|
-
if (excludeExtensions.has(extension)) continue;
|
|
1093
|
-
effectiveIncludeExtensions.add(extension);
|
|
1094
|
-
}
|
|
1095
|
-
return {
|
|
1096
|
-
includeExtensions,
|
|
1097
|
-
excludeExtensions,
|
|
1098
|
-
effectiveIncludeExtensions
|
|
1099
|
-
};
|
|
1100
|
-
}
|
|
1101
|
-
function shouldIncludeFromDirectory(filePath, filter) {
|
|
1102
|
-
const extension = extname(filePath).toLowerCase();
|
|
1103
|
-
return filter.effectiveIncludeExtensions.has(extension);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
//#endregion
|
|
1107
|
-
//#region src/cli/path/load.ts
|
|
1108
|
-
function isProbablyBinary(buffer) {
|
|
1109
|
-
if (buffer.length === 0) return false;
|
|
1110
|
-
const sampleSize = Math.min(buffer.length, 1024);
|
|
1111
|
-
let suspicious = 0;
|
|
1112
|
-
for (let index = 0; index < sampleSize; index += 1) {
|
|
1113
|
-
const byte = buffer[index] ?? 0;
|
|
1114
|
-
if (byte === 0) return true;
|
|
1115
|
-
if (byte === 9 || byte === 10 || byte === 13) continue;
|
|
1116
|
-
if (byte >= 32 && byte <= 126) continue;
|
|
1117
|
-
if (byte >= 128) continue;
|
|
1118
|
-
suspicious += 1;
|
|
1119
|
-
}
|
|
1120
|
-
return suspicious / sampleSize > .3;
|
|
1121
|
-
}
|
|
1122
|
-
async function loadBatchInputs(filePaths) {
|
|
1123
|
-
const files = [];
|
|
1124
|
-
const skipped = [];
|
|
1125
|
-
for (const filePath of filePaths) {
|
|
1126
|
-
let buffer;
|
|
1127
|
-
try {
|
|
1128
|
-
buffer = await readFile(filePath);
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1131
|
-
skipped.push({
|
|
1132
|
-
path: filePath,
|
|
1133
|
-
reason: `not readable: ${message}`
|
|
1134
|
-
});
|
|
1135
|
-
continue;
|
|
1136
|
-
}
|
|
1137
|
-
if (isProbablyBinary(buffer)) {
|
|
1138
|
-
skipped.push({
|
|
1139
|
-
path: filePath,
|
|
1140
|
-
reason: "binary file"
|
|
1141
|
-
});
|
|
1142
|
-
continue;
|
|
1143
|
-
}
|
|
1144
|
-
files.push({
|
|
1145
|
-
path: filePath,
|
|
1146
|
-
content: buffer.toString("utf8")
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
return {
|
|
1150
|
-
files,
|
|
1151
|
-
skipped
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
//#endregion
|
|
1156
|
-
//#region src/cli/path/resolve.ts
|
|
1157
|
-
async function expandDirectory(directoryPath, recursive, filter, skipped) {
|
|
1158
|
-
let entries;
|
|
1159
|
-
try {
|
|
1160
|
-
entries = await readdir(directoryPath, {
|
|
1161
|
-
withFileTypes: true,
|
|
1162
|
-
encoding: "utf8"
|
|
1163
|
-
});
|
|
1164
|
-
} catch (error) {
|
|
1165
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1166
|
-
skipped.push({
|
|
1167
|
-
path: directoryPath,
|
|
1168
|
-
reason: `directory read failed: ${message}`
|
|
1169
|
-
});
|
|
1170
|
-
return [];
|
|
1171
|
-
}
|
|
1172
|
-
const sortedEntries = entries.slice().sort((left, right) => left.name.localeCompare(right.name));
|
|
1173
|
-
const files = [];
|
|
1174
|
-
for (const entry of sortedEntries) {
|
|
1175
|
-
const entryPath = resolve(directoryPath, entry.name);
|
|
1176
|
-
if (entry.isFile()) {
|
|
1177
|
-
if (!shouldIncludeFromDirectory(entryPath, filter)) {
|
|
1178
|
-
skipped.push({
|
|
1179
|
-
path: entryPath,
|
|
1180
|
-
reason: "extension excluded"
|
|
1181
|
-
});
|
|
1182
|
-
continue;
|
|
1183
|
-
}
|
|
1184
|
-
files.push(entryPath);
|
|
1185
|
-
continue;
|
|
1186
|
-
}
|
|
1187
|
-
if (!entry.isDirectory() || !recursive) continue;
|
|
1188
|
-
appendAll(files, await expandDirectory(entryPath, recursive, filter, skipped));
|
|
1189
|
-
}
|
|
1190
|
-
return files;
|
|
1191
|
-
}
|
|
1192
|
-
async function resolveBatchFilePaths(pathInputs, options) {
|
|
1193
|
-
const skipped = [];
|
|
1194
|
-
const resolvedFiles = /* @__PURE__ */ new Set();
|
|
1195
|
-
const extensionFilter = options.extensionFilter ?? buildDirectoryExtensionFilter(void 0, void 0);
|
|
1196
|
-
for (const rawPath of pathInputs) {
|
|
1197
|
-
const targetPath = resolve(rawPath);
|
|
1198
|
-
let metadata;
|
|
1199
|
-
try {
|
|
1200
|
-
metadata = await stat(targetPath);
|
|
1201
|
-
} catch (error) {
|
|
1202
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1203
|
-
skipped.push({
|
|
1204
|
-
path: targetPath,
|
|
1205
|
-
reason: `not readable: ${message}`
|
|
1206
|
-
});
|
|
1207
|
-
continue;
|
|
1208
|
-
}
|
|
1209
|
-
if (metadata.isDirectory() && options.pathMode === "auto") {
|
|
1210
|
-
const files = await expandDirectory(targetPath, options.recursive, extensionFilter, skipped);
|
|
1211
|
-
for (const file of files) resolvedFiles.add(file);
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
if (!metadata.isFile()) {
|
|
1215
|
-
skipped.push({
|
|
1216
|
-
path: targetPath,
|
|
1217
|
-
reason: "not a regular file"
|
|
1218
|
-
});
|
|
1219
|
-
continue;
|
|
1220
|
-
}
|
|
1221
|
-
resolvedFiles.add(targetPath);
|
|
1222
|
-
}
|
|
1223
|
-
return {
|
|
1224
|
-
files: [...resolvedFiles].sort((left, right) => left.localeCompare(right)),
|
|
1225
|
-
skipped
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
//#endregion
|
|
1230
|
-
//#region src/cli/progress/reporter.ts
|
|
1231
|
-
const PROGRESS_BAR_WIDTH = 20;
|
|
1232
|
-
const FILLED_BAR_CHAR = "█";
|
|
1233
|
-
const EMPTY_BAR_CHAR = "░";
|
|
1234
|
-
function clamp(value, min, max) {
|
|
1235
|
-
return Math.max(min, Math.min(max, value));
|
|
1236
|
-
}
|
|
1237
|
-
function buildProgressBar(completed, total) {
|
|
1238
|
-
const safeTotal = Math.max(total, 1);
|
|
1239
|
-
const ratio = clamp(completed / safeTotal, 0, 1);
|
|
1240
|
-
const filled = completed >= safeTotal ? PROGRESS_BAR_WIDTH : Math.floor(ratio * PROGRESS_BAR_WIDTH);
|
|
1241
|
-
const empty = PROGRESS_BAR_WIDTH - filled;
|
|
1242
|
-
return `${FILLED_BAR_CHAR.repeat(filled)}${EMPTY_BAR_CHAR.repeat(empty)}`;
|
|
1243
|
-
}
|
|
1244
|
-
function formatElapsed(startedAtMs) {
|
|
1245
|
-
const elapsedMs = Date.now() - startedAtMs;
|
|
1246
|
-
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
1247
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
1248
|
-
const seconds = totalSeconds % 60;
|
|
1249
|
-
const tenths = Math.floor(Math.max(0, elapsedMs) % 1e3 / 100);
|
|
1250
|
-
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}.${tenths}`;
|
|
1807
|
+
name: key,
|
|
1808
|
+
source: "frontmatter",
|
|
1809
|
+
result: wc_default(valueText ? `${key}: ${valueText}` : key, options)
|
|
1810
|
+
};
|
|
1811
|
+
});
|
|
1251
1812
|
}
|
|
1252
|
-
function
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1813
|
+
function buildSingleItem(name, text, mode, options, source) {
|
|
1814
|
+
return [{
|
|
1815
|
+
name,
|
|
1816
|
+
source,
|
|
1817
|
+
result: wc_default(text, options)
|
|
1818
|
+
}];
|
|
1256
1819
|
}
|
|
1257
|
-
function
|
|
1258
|
-
return
|
|
1820
|
+
function sumTotals(items) {
|
|
1821
|
+
return items.reduce((sum, item) => sum + item.result.total, 0);
|
|
1259
1822
|
}
|
|
1260
|
-
function
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
}
|
|
1275
|
-
const
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
}
|
|
1285
|
-
lastLineLength = line.length;
|
|
1286
|
-
options.stream.write(`${line}\n`);
|
|
1287
|
-
};
|
|
1288
|
-
const clearLine = () => {
|
|
1289
|
-
if (lastLineLength === 0) return;
|
|
1290
|
-
options.stream.write(`\r${" ".repeat(lastLineLength)}\r`);
|
|
1291
|
-
lastLineLength = 0;
|
|
1292
|
-
};
|
|
1823
|
+
function countSections(input, section, options = {}) {
|
|
1824
|
+
const mode = options.mode ?? "chunk";
|
|
1825
|
+
if (section === "all") {
|
|
1826
|
+
const result = wc_default(input, options);
|
|
1827
|
+
return {
|
|
1828
|
+
section,
|
|
1829
|
+
total: result.total,
|
|
1830
|
+
frontmatterType: null,
|
|
1831
|
+
items: [{
|
|
1832
|
+
name: "all",
|
|
1833
|
+
source: "content",
|
|
1834
|
+
result
|
|
1835
|
+
}]
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
const parsed = parseMarkdown(input);
|
|
1839
|
+
const frontmatterText = parsed.frontmatter ?? "";
|
|
1840
|
+
const contentText = parsed.content ?? "";
|
|
1841
|
+
let items = [];
|
|
1842
|
+
if (section === "frontmatter") items = buildSingleItem("frontmatter", frontmatterText, mode, options, "frontmatter");
|
|
1843
|
+
else if (section === "content") items = buildSingleItem("content", contentText, mode, options, "content");
|
|
1844
|
+
else if (section === "split") items = [...buildSingleItem("frontmatter", frontmatterText, mode, options, "frontmatter"), ...buildSingleItem("content", contentText, mode, options, "content")];
|
|
1845
|
+
else if (section === "per-key") items = buildPerKeyItems(parsed.data, mode, options);
|
|
1846
|
+
else if (section === "split-per-key") items = [...buildPerKeyItems(parsed.data, mode, options), ...buildSingleItem("content", contentText, mode, options, "content")];
|
|
1293
1847
|
return {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
active = true;
|
|
1299
|
-
startedAtMs = nextStartedAtMs ?? Date.now();
|
|
1300
|
-
lastRenderedPercent = -1;
|
|
1301
|
-
finalizingStarted = false;
|
|
1302
|
-
render(0);
|
|
1303
|
-
},
|
|
1304
|
-
advance(snapshot) {
|
|
1305
|
-
if (!active) return;
|
|
1306
|
-
render(snapshot.completed);
|
|
1307
|
-
},
|
|
1308
|
-
startFinalizing() {
|
|
1309
|
-
if (!active || finalizingStarted) return;
|
|
1310
|
-
finalizingStarted = true;
|
|
1311
|
-
const line = buildFinalizingLine(startedAtMs);
|
|
1312
|
-
if (isTTY) {
|
|
1313
|
-
if (!clearOnFinish) {
|
|
1314
|
-
options.stream.write(`\n${line}`);
|
|
1315
|
-
lastLineLength = line.length;
|
|
1316
|
-
return;
|
|
1317
|
-
}
|
|
1318
|
-
writeTTYLine(line);
|
|
1319
|
-
return;
|
|
1320
|
-
}
|
|
1321
|
-
lastLineLength = line.length;
|
|
1322
|
-
options.stream.write(`${line}\n`);
|
|
1323
|
-
},
|
|
1324
|
-
finish() {
|
|
1325
|
-
if (!active) return;
|
|
1326
|
-
if (isTTY) if (clearOnFinish) clearLine();
|
|
1327
|
-
else options.stream.write("\n");
|
|
1328
|
-
active = false;
|
|
1329
|
-
}
|
|
1848
|
+
section,
|
|
1849
|
+
total: sumTotals(items),
|
|
1850
|
+
frontmatterType: parsed.frontmatterType,
|
|
1851
|
+
items
|
|
1330
1852
|
};
|
|
1331
1853
|
}
|
|
1332
1854
|
|
|
@@ -1364,6 +1886,43 @@ function mergeWordCounterResult(left, right, preserveCollectorSegments) {
|
|
|
1364
1886
|
items: [...left.breakdown.items, ...right.breakdown.items]
|
|
1365
1887
|
}
|
|
1366
1888
|
};
|
|
1889
|
+
if (left.breakdown.mode === "char-collector" && right.breakdown.mode === "char-collector") {
|
|
1890
|
+
const localeOrder = [];
|
|
1891
|
+
const mergedByLocale = /* @__PURE__ */ new Map();
|
|
1892
|
+
const addItems = (items) => {
|
|
1893
|
+
for (const item of items) {
|
|
1894
|
+
const existing = mergedByLocale.get(item.locale);
|
|
1895
|
+
if (existing) {
|
|
1896
|
+
existing.chars += item.chars;
|
|
1897
|
+
if (item.nonWords) {
|
|
1898
|
+
if (!existing.nonWords) existing.nonWords = createNonWordCollection();
|
|
1899
|
+
mergeNonWordCollections(existing.nonWords, item.nonWords);
|
|
1900
|
+
}
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
localeOrder.push(item.locale);
|
|
1904
|
+
mergedByLocale.set(item.locale, {
|
|
1905
|
+
locale: item.locale,
|
|
1906
|
+
chars: item.chars,
|
|
1907
|
+
nonWords: item.nonWords ? mergeNonWordCollections(createNonWordCollection(), item.nonWords) : void 0
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
addItems(left.breakdown.items);
|
|
1912
|
+
addItems(right.breakdown.items);
|
|
1913
|
+
return {
|
|
1914
|
+
total,
|
|
1915
|
+
counts,
|
|
1916
|
+
breakdown: {
|
|
1917
|
+
mode: "char-collector",
|
|
1918
|
+
items: localeOrder.map((locale) => {
|
|
1919
|
+
const value = mergedByLocale.get(locale);
|
|
1920
|
+
if (!value) throw new Error(`Missing char-collector entry for locale: ${locale}`);
|
|
1921
|
+
return value;
|
|
1922
|
+
})
|
|
1923
|
+
}
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1367
1926
|
if (left.breakdown.mode === "collector" && right.breakdown.mode === "collector") {
|
|
1368
1927
|
const localeOrder = [];
|
|
1369
1928
|
const mergedByLocale = /* @__PURE__ */ new Map();
|
|
@@ -1526,7 +2085,8 @@ async function runBatchCount(options) {
|
|
|
1526
2085
|
const resolved = await resolveBatchFilePaths(options.pathInputs, {
|
|
1527
2086
|
pathMode: options.batchOptions.pathMode,
|
|
1528
2087
|
recursive: options.batchOptions.recursive,
|
|
1529
|
-
extensionFilter: options.extensionFilter
|
|
2088
|
+
extensionFilter: options.extensionFilter,
|
|
2089
|
+
debug: options.debug
|
|
1530
2090
|
});
|
|
1531
2091
|
const resolveElapsedMs = Date.now() - resolveStartedAtMs;
|
|
1532
2092
|
options.debug.emit("batch.resolve.complete", {
|
|
@@ -1608,199 +2168,18 @@ async function runBatchCount(options) {
|
|
|
1608
2168
|
}
|
|
1609
2169
|
|
|
1610
2170
|
//#endregion
|
|
1611
|
-
//#region src/
|
|
1612
|
-
function
|
|
1613
|
-
return {
|
|
1614
|
-
|
|
1615
|
-
emit(event, details = {}) {
|
|
1616
|
-
if (!enabled) return;
|
|
1617
|
-
const payload = {
|
|
1618
|
-
event,
|
|
1619
|
-
...details
|
|
1620
|
-
};
|
|
1621
|
-
console.error(`[debug] ${JSON.stringify(payload)}`);
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
//#endregion
|
|
1627
|
-
//#region src/utils/show-singular-or-plural-word.ts
|
|
1628
|
-
function showSingularOrPluralWord(count, word) {
|
|
1629
|
-
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
//#endregion
|
|
1633
|
-
//#region src/cli/total-of.ts
|
|
1634
|
-
const TOTAL_OF_PARTS = [
|
|
1635
|
-
"words",
|
|
1636
|
-
"emoji",
|
|
1637
|
-
"symbols",
|
|
1638
|
-
"punctuation",
|
|
1639
|
-
"whitespace"
|
|
1640
|
-
];
|
|
1641
|
-
const TOTAL_OF_PART_ALIASES = {
|
|
1642
|
-
word: "words",
|
|
1643
|
-
words: "words",
|
|
1644
|
-
emoji: "emoji",
|
|
1645
|
-
emojis: "emoji",
|
|
1646
|
-
symbol: "symbols",
|
|
1647
|
-
symbols: "symbols",
|
|
1648
|
-
punction: "punctuation",
|
|
1649
|
-
punctuation: "punctuation",
|
|
1650
|
-
whitespace: "whitespace"
|
|
1651
|
-
};
|
|
1652
|
-
function createTotalOfCounts() {
|
|
1653
|
-
return {
|
|
1654
|
-
words: 0,
|
|
1655
|
-
emoji: 0,
|
|
1656
|
-
symbols: 0,
|
|
1657
|
-
punctuation: 0,
|
|
1658
|
-
whitespace: 0
|
|
1659
|
-
};
|
|
1660
|
-
}
|
|
1661
|
-
function collectNonWordCounts(target, nonWords) {
|
|
1662
|
-
if (!nonWords) return;
|
|
1663
|
-
target.emoji += nonWords.counts.emoji;
|
|
1664
|
-
target.symbols += nonWords.counts.symbols;
|
|
1665
|
-
target.punctuation += nonWords.counts.punctuation;
|
|
1666
|
-
target.whitespace += nonWords.counts.whitespace ?? 0;
|
|
1667
|
-
}
|
|
1668
|
-
function collectFromWordCounterResult(result) {
|
|
1669
|
-
const counts = createTotalOfCounts();
|
|
1670
|
-
counts.words += result.counts?.words ?? result.total;
|
|
1671
|
-
if (result.breakdown.mode === "collector") {
|
|
1672
|
-
collectNonWordCounts(counts, result.breakdown.nonWords);
|
|
1673
|
-
return counts;
|
|
1674
|
-
}
|
|
1675
|
-
for (const item of result.breakdown.items) collectNonWordCounts(counts, item.nonWords);
|
|
1676
|
-
return counts;
|
|
1677
|
-
}
|
|
1678
|
-
function collectTotalOfCounts(result) {
|
|
1679
|
-
if (!("section" in result)) return collectFromWordCounterResult(result);
|
|
1680
|
-
const counts = createTotalOfCounts();
|
|
1681
|
-
for (const item of result.items) {
|
|
1682
|
-
const itemCounts = collectFromWordCounterResult(item.result);
|
|
1683
|
-
for (const part of TOTAL_OF_PARTS) counts[part] += itemCounts[part];
|
|
1684
|
-
}
|
|
1685
|
-
return counts;
|
|
1686
|
-
}
|
|
1687
|
-
function parseTotalOfToken(token) {
|
|
1688
|
-
const canonical = TOTAL_OF_PART_ALIASES[token.trim().toLowerCase()];
|
|
1689
|
-
if (canonical) return canonical;
|
|
1690
|
-
throw new Error(`Invalid --total-of part: ${token}. Allowed: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
1691
|
-
}
|
|
1692
|
-
function parseTotalOfOption(value) {
|
|
1693
|
-
const rawTokens = value.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
1694
|
-
if (rawTokens.length === 0) throw new Error(`Invalid --total-of value: "${value}". Use comma-separated parts from: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
1695
|
-
const parts = [];
|
|
1696
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1697
|
-
for (const token of rawTokens) {
|
|
1698
|
-
const parsed = parseTotalOfToken(token);
|
|
1699
|
-
if (seen.has(parsed)) continue;
|
|
1700
|
-
seen.add(parsed);
|
|
1701
|
-
parts.push(parsed);
|
|
1702
|
-
}
|
|
1703
|
-
return parts;
|
|
1704
|
-
}
|
|
1705
|
-
function requiresNonWordCollection(parts) {
|
|
1706
|
-
if (!parts || parts.length === 0) return false;
|
|
1707
|
-
return parts.some((part) => part !== "words");
|
|
1708
|
-
}
|
|
1709
|
-
function requiresWhitespaceCollection(parts) {
|
|
1710
|
-
if (!parts || parts.length === 0) return false;
|
|
1711
|
-
return parts.includes("whitespace");
|
|
1712
|
-
}
|
|
1713
|
-
function resolveTotalOfOverride(result, parts) {
|
|
1714
|
-
if (!parts || parts.length === 0) return;
|
|
1715
|
-
const counts = collectTotalOfCounts(result);
|
|
1716
|
-
let total = 0;
|
|
1717
|
-
for (const part of parts) total += counts[part];
|
|
1718
|
-
return {
|
|
1719
|
-
parts: [...parts],
|
|
1720
|
-
total
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
function formatTotalOfParts(parts) {
|
|
1724
|
-
return parts.join(", ");
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
//#endregion
|
|
1728
|
-
//#region node_modules/picocolors/picocolors.js
|
|
1729
|
-
var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1730
|
-
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
1731
|
-
let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
1732
|
-
let formatter = (open, close, replace = open) => (input) => {
|
|
1733
|
-
let string = "" + input, index = string.indexOf(close, open.length);
|
|
1734
|
-
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
1735
|
-
};
|
|
1736
|
-
let replaceClose = (string, close, replace, index) => {
|
|
1737
|
-
let result = "", cursor = 0;
|
|
1738
|
-
do {
|
|
1739
|
-
result += string.substring(cursor, index) + replace;
|
|
1740
|
-
cursor = index + close.length;
|
|
1741
|
-
index = string.indexOf(close, cursor);
|
|
1742
|
-
} while (~index);
|
|
1743
|
-
return result + string.substring(cursor);
|
|
1744
|
-
};
|
|
1745
|
-
let createColors = (enabled = isColorSupported) => {
|
|
1746
|
-
let f = enabled ? formatter : () => String;
|
|
1747
|
-
return {
|
|
1748
|
-
isColorSupported: enabled,
|
|
1749
|
-
reset: f("\x1B[0m", "\x1B[0m"),
|
|
1750
|
-
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
1751
|
-
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
1752
|
-
italic: f("\x1B[3m", "\x1B[23m"),
|
|
1753
|
-
underline: f("\x1B[4m", "\x1B[24m"),
|
|
1754
|
-
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
1755
|
-
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
1756
|
-
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
1757
|
-
black: f("\x1B[30m", "\x1B[39m"),
|
|
1758
|
-
red: f("\x1B[31m", "\x1B[39m"),
|
|
1759
|
-
green: f("\x1B[32m", "\x1B[39m"),
|
|
1760
|
-
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
1761
|
-
blue: f("\x1B[34m", "\x1B[39m"),
|
|
1762
|
-
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
1763
|
-
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
1764
|
-
white: f("\x1B[37m", "\x1B[39m"),
|
|
1765
|
-
gray: f("\x1B[90m", "\x1B[39m"),
|
|
1766
|
-
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
1767
|
-
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
1768
|
-
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
1769
|
-
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
1770
|
-
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
1771
|
-
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
1772
|
-
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
1773
|
-
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
1774
|
-
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
1775
|
-
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
1776
|
-
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
1777
|
-
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
1778
|
-
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
1779
|
-
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
1780
|
-
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
1781
|
-
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
1782
|
-
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
1783
|
-
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
1784
|
-
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
1785
|
-
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
1786
|
-
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
1787
|
-
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
1788
|
-
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
1789
|
-
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
1790
|
-
};
|
|
1791
|
-
};
|
|
1792
|
-
module.exports = createColors();
|
|
1793
|
-
module.exports.createColors = createColors;
|
|
1794
|
-
}));
|
|
2171
|
+
//#region src/utils/show-singular-or-plural-word.ts
|
|
2172
|
+
function showSingularOrPluralWord(count, word) {
|
|
2173
|
+
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
2174
|
+
}
|
|
1795
2175
|
|
|
1796
2176
|
//#endregion
|
|
1797
2177
|
//#region src/cli/output/render.ts
|
|
1798
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
1799
2178
|
function getCountUnit(mode) {
|
|
1800
|
-
return mode === "char" ? "character" : "word";
|
|
2179
|
+
return mode === "char" || mode === "char-collector" ? "character" : "word";
|
|
1801
2180
|
}
|
|
1802
2181
|
function getTotalLabels(mode, includeNonWords) {
|
|
1803
|
-
const unit = mode === "char" ? "characters" : "words";
|
|
2182
|
+
const unit = mode === "char" || mode === "char-collector" ? "characters" : "words";
|
|
1804
2183
|
if (includeNonWords) return {
|
|
1805
2184
|
overall: "Total count",
|
|
1806
2185
|
section: "total count"
|
|
@@ -1856,7 +2235,7 @@ function renderStandardResult(result, totalLabel, totalOfOverride) {
|
|
|
1856
2235
|
renderNonWords(result.breakdown.nonWords, false);
|
|
1857
2236
|
return;
|
|
1858
2237
|
}
|
|
1859
|
-
if (result.breakdown.mode === "char") {
|
|
2238
|
+
if (result.breakdown.mode === "char" || result.breakdown.mode === "char-collector") {
|
|
1860
2239
|
renderCountBreakdown(result.breakdown.items.map((item) => ({
|
|
1861
2240
|
locale: item.locale,
|
|
1862
2241
|
count: item.chars,
|
|
@@ -1894,7 +2273,7 @@ function renderStandardSectionedResult(result, labels, totalOfOverride) {
|
|
|
1894
2273
|
renderNonWords(item.result.breakdown.nonWords, false);
|
|
1895
2274
|
continue;
|
|
1896
2275
|
}
|
|
1897
|
-
if (item.result.breakdown.mode === "char") {
|
|
2276
|
+
if (item.result.breakdown.mode === "char" || item.result.breakdown.mode === "char-collector") {
|
|
1898
2277
|
renderCountBreakdown(item.result.breakdown.items.map((chunk) => ({
|
|
1899
2278
|
locale: chunk.locale,
|
|
1900
2279
|
count: chunk.chars,
|
|
@@ -1922,92 +2301,25 @@ function reportSkipped(skipped) {
|
|
|
1922
2301
|
console.error(import_picocolors.default.yellow(`Skipped ${skipped.length} path(s):`));
|
|
1923
2302
|
for (const item of skipped) console.error(import_picocolors.default.yellow(`- ${toDisplayPath(item.path)} (${item.reason})`));
|
|
1924
2303
|
}
|
|
1925
|
-
function renderPerFileStandard(summary, labels, resolveTotalOfOverride
|
|
2304
|
+
function renderPerFileStandard(summary, labels, resolveTotalOfOverride) {
|
|
1926
2305
|
for (const file of summary.files) {
|
|
1927
2306
|
console.log(import_picocolors.default.bold(`[File] ${toDisplayPath(file.path)}`));
|
|
1928
2307
|
if (isSectionedResult(file.result)) {
|
|
1929
|
-
renderStandardSectionedResult(file.result, labels, resolveTotalOfOverride
|
|
2308
|
+
renderStandardSectionedResult(file.result, labels, resolveTotalOfOverride?.(file.result));
|
|
1930
2309
|
continue;
|
|
1931
2310
|
}
|
|
1932
|
-
renderStandardResult(file.result, labels.overall, resolveTotalOfOverride
|
|
2311
|
+
renderStandardResult(file.result, labels.overall, resolveTotalOfOverride?.(file.result));
|
|
1933
2312
|
}
|
|
1934
2313
|
console.log(import_picocolors.default.bold(`[Merged] ${summary.files.length} file(s)`));
|
|
1935
2314
|
if (isSectionedResult(summary.aggregate)) {
|
|
1936
|
-
renderStandardSectionedResult(summary.aggregate, labels, resolveTotalOfOverride
|
|
2315
|
+
renderStandardSectionedResult(summary.aggregate, labels, resolveTotalOfOverride?.(summary.aggregate));
|
|
1937
2316
|
return;
|
|
1938
2317
|
}
|
|
1939
|
-
renderStandardResult(summary.aggregate, labels.overall, resolveTotalOfOverride
|
|
2318
|
+
renderStandardResult(summary.aggregate, labels.overall, resolveTotalOfOverride?.(summary.aggregate));
|
|
1940
2319
|
}
|
|
1941
2320
|
|
|
1942
2321
|
//#endregion
|
|
1943
|
-
//#region src/
|
|
1944
|
-
const MODE_CHOICES = [
|
|
1945
|
-
"chunk",
|
|
1946
|
-
"segments",
|
|
1947
|
-
"collector",
|
|
1948
|
-
"char"
|
|
1949
|
-
];
|
|
1950
|
-
const FORMAT_CHOICES = [
|
|
1951
|
-
"standard",
|
|
1952
|
-
"raw",
|
|
1953
|
-
"json"
|
|
1954
|
-
];
|
|
1955
|
-
const SECTION_CHOICES = [
|
|
1956
|
-
"all",
|
|
1957
|
-
"split",
|
|
1958
|
-
"frontmatter",
|
|
1959
|
-
"content",
|
|
1960
|
-
"per-key",
|
|
1961
|
-
"split-per-key"
|
|
1962
|
-
];
|
|
1963
|
-
const PATH_MODE_CHOICES = ["auto", "manual"];
|
|
1964
|
-
function getPackageVersion() {
|
|
1965
|
-
const packageCandidates = [new URL("../package.json", import.meta.url), new URL("../../package.json", import.meta.url)];
|
|
1966
|
-
let version = "0.0.0";
|
|
1967
|
-
for (const packageUrl of packageCandidates) try {
|
|
1968
|
-
const raw = readFileSync(packageUrl, "utf8");
|
|
1969
|
-
const data = JSON.parse(raw);
|
|
1970
|
-
if (data.version) {
|
|
1971
|
-
version = data.version;
|
|
1972
|
-
break;
|
|
1973
|
-
}
|
|
1974
|
-
} catch {
|
|
1975
|
-
continue;
|
|
1976
|
-
}
|
|
1977
|
-
return import_picocolors.default.bgBlack(import_picocolors.default.bold(import_picocolors.default.italic(` word-counter ${import_picocolors.default.cyanBright(`ver.${version}`)} `)));
|
|
1978
|
-
}
|
|
1979
|
-
async function readStdin() {
|
|
1980
|
-
if (process.stdin.isTTY) return "";
|
|
1981
|
-
return new Promise((resolve$1, reject) => {
|
|
1982
|
-
const chunks = [];
|
|
1983
|
-
process.stdin.setEncoding("utf8");
|
|
1984
|
-
process.stdin.on("data", (chunk) => chunks.push(String(chunk)));
|
|
1985
|
-
process.stdin.on("end", () => resolve$1(chunks.join("")));
|
|
1986
|
-
process.stdin.on("error", (error) => reject(error));
|
|
1987
|
-
process.stdin.resume();
|
|
1988
|
-
});
|
|
1989
|
-
}
|
|
1990
|
-
async function resolveInput(textTokens) {
|
|
1991
|
-
if (textTokens.length > 0) return textTokens.join(" ");
|
|
1992
|
-
return readStdin();
|
|
1993
|
-
}
|
|
1994
|
-
function collectPathValue(value, previous = []) {
|
|
1995
|
-
return [...previous, value];
|
|
1996
|
-
}
|
|
1997
|
-
function resolveBatchScope(argv) {
|
|
1998
|
-
let scope = "merged";
|
|
1999
|
-
for (const token of argv) {
|
|
2000
|
-
if (token === "--merged") {
|
|
2001
|
-
scope = "merged";
|
|
2002
|
-
continue;
|
|
2003
|
-
}
|
|
2004
|
-
if (token === "--per-file") scope = "per-file";
|
|
2005
|
-
}
|
|
2006
|
-
return scope;
|
|
2007
|
-
}
|
|
2008
|
-
function hasPathInput(pathValues) {
|
|
2009
|
-
return Array.isArray(pathValues) && pathValues.length > 0;
|
|
2010
|
-
}
|
|
2322
|
+
//#region src/cli/output/normalize-base.ts
|
|
2011
2323
|
function normalizeWordCounterResultBase(result) {
|
|
2012
2324
|
result.total = result.counts?.words ?? result.total;
|
|
2013
2325
|
delete result.counts;
|
|
@@ -2015,7 +2327,7 @@ function normalizeWordCounterResultBase(result) {
|
|
|
2015
2327
|
delete result.breakdown.nonWords;
|
|
2016
2328
|
return result;
|
|
2017
2329
|
}
|
|
2018
|
-
if (result.breakdown.mode === "char") {
|
|
2330
|
+
if (result.breakdown.mode === "char" || result.breakdown.mode === "char-collector") {
|
|
2019
2331
|
for (const item of result.breakdown.items) {
|
|
2020
2332
|
const nonWordCount = (item.nonWords?.counts.emoji ?? 0) + (item.nonWords?.counts.symbols ?? 0) + (item.nonWords?.counts.punctuation ?? 0) + (item.nonWords?.counts.whitespace ?? 0);
|
|
2021
2333
|
item.chars = Math.max(0, item.chars - nonWordCount);
|
|
@@ -2044,24 +2356,41 @@ function normalizeBatchSummaryBase(summary) {
|
|
|
2044
2356
|
normalizeResultBase(summary.aggregate);
|
|
2045
2357
|
return summary;
|
|
2046
2358
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2359
|
+
|
|
2360
|
+
//#endregion
|
|
2361
|
+
//#region src/cli/runtime/options.ts
|
|
2362
|
+
function hasPathInput(pathValues) {
|
|
2363
|
+
return Array.isArray(pathValues) && pathValues.length > 0;
|
|
2364
|
+
}
|
|
2365
|
+
function resolveBatchScope(argv) {
|
|
2366
|
+
let scope = "merged";
|
|
2367
|
+
for (const token of argv) {
|
|
2368
|
+
if (token === "--merged") {
|
|
2369
|
+
scope = "merged";
|
|
2370
|
+
continue;
|
|
2371
|
+
}
|
|
2372
|
+
if (token === "--per-file") scope = "per-file";
|
|
2373
|
+
}
|
|
2374
|
+
return scope;
|
|
2375
|
+
}
|
|
2376
|
+
function resolveDebugReportPathOption(rawValue) {
|
|
2377
|
+
if (rawValue === void 0 || rawValue === false) return;
|
|
2378
|
+
if (typeof rawValue === "string") return rawValue;
|
|
2379
|
+
}
|
|
2380
|
+
function resolveCountRunOptions(options) {
|
|
2381
|
+
const useSection = options.section !== "all";
|
|
2382
|
+
const totalOfParts = options.totalOf;
|
|
2383
|
+
const requestedNonWords = Boolean(options.nonWords || options.includeWhitespace || options.misc);
|
|
2384
|
+
const collectNonWordsForOverride = requiresNonWordCollection(totalOfParts);
|
|
2385
|
+
const collectWhitespaceForOverride = requiresWhitespaceCollection(totalOfParts);
|
|
2386
|
+
const enableNonWords = Boolean(options.nonWords || options.includeWhitespace || options.misc || collectNonWordsForOverride);
|
|
2387
|
+
const enableWhitespace = Boolean(options.includeWhitespace || options.misc || collectWhitespaceForOverride);
|
|
2388
|
+
return {
|
|
2389
|
+
useSection,
|
|
2390
|
+
totalOfParts,
|
|
2391
|
+
requestedNonWords,
|
|
2392
|
+
shouldNormalizeBaseOutput: !requestedNonWords && enableNonWords,
|
|
2393
|
+
wcOptions: {
|
|
2065
2394
|
mode: options.mode,
|
|
2066
2395
|
latinLanguageHint: options.latinLanguage,
|
|
2067
2396
|
latinTagHint: options.latinTag,
|
|
@@ -2070,152 +2399,255 @@ async function runCli(argv = process.argv, runtime = {}) {
|
|
|
2070
2399
|
hanTagHint: options.hanTag,
|
|
2071
2400
|
nonWords: enableNonWords,
|
|
2072
2401
|
includeWhitespace: enableWhitespace
|
|
2073
|
-
};
|
|
2074
|
-
if (!hasPathInput(options.path)) {
|
|
2075
|
-
let input;
|
|
2076
|
-
try {
|
|
2077
|
-
input = await resolveInput(textTokens);
|
|
2078
|
-
} catch (error) {
|
|
2079
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2080
|
-
program.error(`Failed to read input: ${message}`);
|
|
2081
|
-
return;
|
|
2082
|
-
}
|
|
2083
|
-
const trimmed = input.trim();
|
|
2084
|
-
if (!trimmed) {
|
|
2085
|
-
program.error(import_picocolors.default.red("No input provided. Pass text, pipe stdin, or use --path."));
|
|
2086
|
-
return;
|
|
2087
|
-
}
|
|
2088
|
-
const result = useSection ? countSections(trimmed, options.section, wcOptions) : wc_default(trimmed, wcOptions);
|
|
2089
|
-
const totalOfOverride = resolveTotalOfOverride(result, totalOfParts);
|
|
2090
|
-
const displayResult = shouldNormalizeBaseOutput ? normalizeResultBase(result) : result;
|
|
2091
|
-
if (options.format === "raw") {
|
|
2092
|
-
console.log(totalOfOverride?.total ?? displayResult.total);
|
|
2093
|
-
return;
|
|
2094
|
-
}
|
|
2095
|
-
if (options.format === "json") {
|
|
2096
|
-
const spacing = options.pretty ? 2 : 0;
|
|
2097
|
-
if (!totalOfOverride) {
|
|
2098
|
-
console.log(JSON.stringify(displayResult, null, spacing));
|
|
2099
|
-
return;
|
|
2100
|
-
}
|
|
2101
|
-
console.log(JSON.stringify({
|
|
2102
|
-
...displayResult,
|
|
2103
|
-
meta: {
|
|
2104
|
-
totalOf: totalOfOverride.parts,
|
|
2105
|
-
totalOfOverride: totalOfOverride.total
|
|
2106
|
-
}
|
|
2107
|
-
}, null, spacing));
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
const labels$1 = getTotalLabels(options.mode, requestedNonWords);
|
|
2111
|
-
if (isSectionedResult(displayResult)) {
|
|
2112
|
-
renderStandardSectionedResult(displayResult, labels$1, totalOfOverride);
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
renderStandardResult(displayResult, labels$1.overall, totalOfOverride);
|
|
2116
|
-
return;
|
|
2117
2402
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
function formatInputReadError(error) {
|
|
2406
|
+
return `Failed to read input: ${error instanceof Error ? error.message : String(error)}`;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
//#endregion
|
|
2410
|
+
//#region src/cli/runtime/batch.ts
|
|
2411
|
+
async function executeBatchCount({ argv, options, runtime, resolved, debug, teeEnabled }) {
|
|
2412
|
+
const batchOptions = {
|
|
2413
|
+
scope: resolveBatchScope(argv),
|
|
2414
|
+
pathMode: options.pathMode,
|
|
2415
|
+
recursive: options.recursive,
|
|
2416
|
+
quietSkips: Boolean(options.quietSkips)
|
|
2417
|
+
};
|
|
2418
|
+
const extensionFilter = buildDirectoryExtensionFilter(options.includeExt, options.excludeExt);
|
|
2419
|
+
const debugEnabled = Boolean(options.debug);
|
|
2420
|
+
const mirrorDebugToTerminal = debugEnabled && (!debug.reportPath || teeEnabled);
|
|
2421
|
+
const summary = await runBatchCount({
|
|
2422
|
+
pathInputs: options.path ?? [],
|
|
2423
|
+
batchOptions,
|
|
2424
|
+
extensionFilter,
|
|
2425
|
+
section: options.section,
|
|
2426
|
+
wcOptions: resolved.wcOptions,
|
|
2427
|
+
preserveCollectorSegments: options.format === "json",
|
|
2428
|
+
debug,
|
|
2429
|
+
progressReporter: createBatchProgressReporter({
|
|
2430
|
+
enabled: options.format === "standard" && options.progress,
|
|
2431
|
+
stream: runtime.stderr ?? process.stderr,
|
|
2432
|
+
clearOnFinish: !(mirrorDebugToTerminal || options.keepProgress)
|
|
2433
|
+
})
|
|
2434
|
+
});
|
|
2435
|
+
const showSkipDiagnostics = debugEnabled && !batchOptions.quietSkips;
|
|
2436
|
+
debug.emit("batch.skips.policy", {
|
|
2437
|
+
enabled: showSkipDiagnostics,
|
|
2438
|
+
quietSkips: batchOptions.quietSkips
|
|
2439
|
+
});
|
|
2440
|
+
if (showSkipDiagnostics) {
|
|
2441
|
+
debug.emit("batch.skips.report", { count: summary.skipped.length });
|
|
2442
|
+
if (options.verbose) for (const skip of summary.skipped) debug.emit("batch.skips.item", {
|
|
2443
|
+
path: skip.path,
|
|
2444
|
+
reason: skip.reason
|
|
2445
|
+
}, { verbosity: "verbose" });
|
|
2446
|
+
if (mirrorDebugToTerminal) reportSkipped(summary.skipped);
|
|
2447
|
+
}
|
|
2448
|
+
if (summary.files.length === 0) throw new Error("No readable text-like inputs were found from --path.");
|
|
2449
|
+
let aggregateTotalOfOverride;
|
|
2450
|
+
let totalOfOverridesByResult;
|
|
2451
|
+
if (resolved.totalOfParts && resolved.totalOfParts.length > 0) {
|
|
2452
|
+
totalOfOverridesByResult = /* @__PURE__ */ new WeakMap();
|
|
2453
|
+
const aggregateOverride = resolveTotalOfOverride(summary.aggregate, resolved.totalOfParts);
|
|
2454
|
+
if (aggregateOverride) {
|
|
2455
|
+
totalOfOverridesByResult.set(summary.aggregate, aggregateOverride);
|
|
2456
|
+
aggregateTotalOfOverride = aggregateOverride;
|
|
2457
|
+
}
|
|
2458
|
+
for (const file of summary.files) {
|
|
2459
|
+
const fileOverride = resolveTotalOfOverride(file.result, resolved.totalOfParts);
|
|
2460
|
+
if (!fileOverride) continue;
|
|
2461
|
+
totalOfOverridesByResult.set(file.result, fileOverride);
|
|
2462
|
+
}
|
|
2463
|
+
} else aggregateTotalOfOverride = resolveTotalOfOverride(summary.aggregate, resolved.totalOfParts);
|
|
2464
|
+
if (resolved.shouldNormalizeBaseOutput) normalizeBatchSummaryBase(summary);
|
|
2465
|
+
if (!aggregateTotalOfOverride && totalOfOverridesByResult) aggregateTotalOfOverride = totalOfOverridesByResult.get(summary.aggregate);
|
|
2466
|
+
if (options.format === "raw") {
|
|
2467
|
+
console.log(aggregateTotalOfOverride?.total ?? summary.aggregate.total);
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
if (options.format === "json") {
|
|
2471
|
+
const spacing = options.pretty ? 2 : 0;
|
|
2472
|
+
if (batchOptions.scope === "per-file") {
|
|
2473
|
+
const skipped = showSkipDiagnostics ? summary.skipped : void 0;
|
|
2474
|
+
const meta = resolved.totalOfParts && resolved.totalOfParts.length > 0 ? {
|
|
2475
|
+
totalOf: resolved.totalOfParts,
|
|
2476
|
+
aggregateTotalOfOverride: aggregateTotalOfOverride?.total ?? summary.aggregate.total
|
|
2477
|
+
} : void 0;
|
|
2478
|
+
const payload = {
|
|
2479
|
+
scope: "per-file",
|
|
2480
|
+
files: summary.files.map((file) => ({
|
|
2481
|
+
path: file.path,
|
|
2482
|
+
result: file.result
|
|
2483
|
+
})),
|
|
2484
|
+
...skipped ? { skipped } : {},
|
|
2485
|
+
aggregate: summary.aggregate,
|
|
2486
|
+
...meta ? { meta } : {}
|
|
2487
|
+
};
|
|
2488
|
+
console.log(JSON.stringify(payload, null, spacing));
|
|
2489
|
+
return;
|
|
2148
2490
|
}
|
|
2149
|
-
if (
|
|
2150
|
-
|
|
2491
|
+
if (!aggregateTotalOfOverride) {
|
|
2492
|
+
console.log(JSON.stringify(summary.aggregate, null, spacing));
|
|
2151
2493
|
return;
|
|
2152
2494
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
if (aggregateOverride) {
|
|
2159
|
-
totalOfOverridesByResult.set(summary.aggregate, aggregateOverride);
|
|
2160
|
-
aggregateTotalOfOverride = aggregateOverride;
|
|
2495
|
+
console.log(JSON.stringify({
|
|
2496
|
+
...summary.aggregate,
|
|
2497
|
+
meta: {
|
|
2498
|
+
totalOf: aggregateTotalOfOverride.parts,
|
|
2499
|
+
totalOfOverride: aggregateTotalOfOverride.total
|
|
2161
2500
|
}
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2501
|
+
}, null, spacing));
|
|
2502
|
+
return;
|
|
2503
|
+
}
|
|
2504
|
+
const labels = getTotalLabels(options.mode, resolved.requestedNonWords);
|
|
2505
|
+
const totalOfResolver = resolved.totalOfParts && resolved.totalOfParts.length > 0 ? (result) => totalOfOverridesByResult?.get(result) ?? resolveTotalOfOverride(result, resolved.totalOfParts) : void 0;
|
|
2506
|
+
if (batchOptions.scope === "per-file") {
|
|
2507
|
+
renderPerFileStandard(summary, labels, totalOfResolver);
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (isSectionedResult(summary.aggregate)) {
|
|
2511
|
+
renderStandardSectionedResult(summary.aggregate, labels, aggregateTotalOfOverride);
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
renderStandardResult(summary.aggregate, labels.overall, aggregateTotalOfOverride);
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
//#endregion
|
|
2518
|
+
//#region src/cli/runtime/input.ts
|
|
2519
|
+
async function readStdin() {
|
|
2520
|
+
if (process.stdin.isTTY) return "";
|
|
2521
|
+
return new Promise((resolve, reject) => {
|
|
2522
|
+
const chunks = [];
|
|
2523
|
+
process.stdin.setEncoding("utf8");
|
|
2524
|
+
process.stdin.on("data", (chunk) => chunks.push(String(chunk)));
|
|
2525
|
+
process.stdin.on("end", () => resolve(chunks.join("")));
|
|
2526
|
+
process.stdin.on("error", (error) => reject(error));
|
|
2527
|
+
process.stdin.resume();
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
async function resolveInput(textTokens) {
|
|
2531
|
+
if (textTokens.length > 0) return textTokens.join(" ");
|
|
2532
|
+
return readStdin();
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
//#endregion
|
|
2536
|
+
//#region src/cli/runtime/single.ts
|
|
2537
|
+
async function executeSingleCount({ textTokens, options, resolved }) {
|
|
2538
|
+
let input;
|
|
2539
|
+
try {
|
|
2540
|
+
input = await resolveInput(textTokens);
|
|
2541
|
+
} catch (error) {
|
|
2542
|
+
throw new Error(formatInputReadError(error));
|
|
2543
|
+
}
|
|
2544
|
+
const trimmed = input.trim();
|
|
2545
|
+
if (!trimmed) throw new Error("No input provided. Pass text, pipe stdin, or use --path.");
|
|
2546
|
+
const result = resolved.useSection ? countSections(trimmed, options.section, resolved.wcOptions) : wc_default(trimmed, resolved.wcOptions);
|
|
2547
|
+
const totalOfOverride = resolveTotalOfOverride(result, resolved.totalOfParts);
|
|
2548
|
+
const displayResult = resolved.shouldNormalizeBaseOutput ? normalizeResultBase(result) : result;
|
|
2549
|
+
if (options.format === "raw") {
|
|
2550
|
+
console.log(totalOfOverride?.total ?? displayResult.total);
|
|
2551
|
+
return;
|
|
2552
|
+
}
|
|
2553
|
+
if (options.format === "json") {
|
|
2554
|
+
const spacing = options.pretty ? 2 : 0;
|
|
2555
|
+
if (!totalOfOverride) {
|
|
2556
|
+
console.log(JSON.stringify(displayResult, null, spacing));
|
|
2172
2557
|
return;
|
|
2173
2558
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
totalOf: totalOfParts,
|
|
2180
|
-
aggregateTotalOfOverride: aggregateTotalOfOverride?.total ?? summary.aggregate.total
|
|
2181
|
-
} : void 0;
|
|
2182
|
-
const payload = {
|
|
2183
|
-
scope: "per-file",
|
|
2184
|
-
files: summary.files.map((file) => ({
|
|
2185
|
-
path: file.path,
|
|
2186
|
-
result: file.result
|
|
2187
|
-
})),
|
|
2188
|
-
...skipped ? { skipped } : {},
|
|
2189
|
-
aggregate: summary.aggregate,
|
|
2190
|
-
...meta ? { meta } : {}
|
|
2191
|
-
};
|
|
2192
|
-
console.log(JSON.stringify(payload, null, spacing));
|
|
2193
|
-
return;
|
|
2194
|
-
}
|
|
2195
|
-
if (!aggregateTotalOfOverride) {
|
|
2196
|
-
console.log(JSON.stringify(summary.aggregate, null, spacing));
|
|
2197
|
-
return;
|
|
2559
|
+
console.log(JSON.stringify({
|
|
2560
|
+
...displayResult,
|
|
2561
|
+
meta: {
|
|
2562
|
+
totalOf: totalOfOverride.parts,
|
|
2563
|
+
totalOfOverride: totalOfOverride.total
|
|
2198
2564
|
}
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2565
|
+
}, null, spacing));
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
const labels = getTotalLabels(options.mode, resolved.requestedNonWords);
|
|
2569
|
+
if (isSectionedResult(displayResult)) {
|
|
2570
|
+
renderStandardSectionedResult(displayResult, labels, totalOfOverride);
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
renderStandardResult(displayResult, labels.overall, totalOfOverride);
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
//#endregion
|
|
2577
|
+
//#region src/command.ts
|
|
2578
|
+
async function runCli(argv = process.argv, runtime = {}) {
|
|
2579
|
+
const program = new Command();
|
|
2580
|
+
const parseMode = (value) => {
|
|
2581
|
+
const normalized = normalizeMode(value);
|
|
2582
|
+
if (!normalized) throw new Error(`Invalid mode: ${value}`);
|
|
2583
|
+
return normalized;
|
|
2584
|
+
};
|
|
2585
|
+
program.name("word-counter").description("Locale-aware word counting powered by Intl.Segmenter.").version(getFormattedVersionLabel(), "-v, --version", "output the version number");
|
|
2586
|
+
configureProgramOptions(program, parseMode);
|
|
2587
|
+
program.action(async (textTokens, options) => {
|
|
2588
|
+
const debugEnabled = Boolean(options.debug);
|
|
2589
|
+
const debugReportPath = resolveDebugReportPathOption(options.debugReport);
|
|
2590
|
+
const debugReportEnabled = options.debugReport !== void 0 && options.debugReport !== false;
|
|
2591
|
+
if (options.verbose && !debugEnabled) {
|
|
2592
|
+
program.error(import_picocolors.default.red("`--verbose` requires `--debug`."));
|
|
2206
2593
|
return;
|
|
2207
2594
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2595
|
+
if (debugReportEnabled && !debugEnabled) {
|
|
2596
|
+
program.error(import_picocolors.default.red("`--debug-report` requires `--debug`."));
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2599
|
+
const teeEnabled = Boolean(options.debugReportTee || options.debugTee);
|
|
2600
|
+
if (teeEnabled && !debugReportEnabled) {
|
|
2601
|
+
program.error(import_picocolors.default.red("`--debug-report-tee` (alias: `--debug-tee`) requires `--debug-report`."));
|
|
2212
2602
|
return;
|
|
2213
2603
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2604
|
+
let debug;
|
|
2605
|
+
try {
|
|
2606
|
+
debug = createDebugChannel({
|
|
2607
|
+
enabled: debugEnabled,
|
|
2608
|
+
verbosity: options.verbose ? "verbose" : "compact",
|
|
2609
|
+
report: debugReportEnabled ? {
|
|
2610
|
+
path: debugReportPath,
|
|
2611
|
+
tee: teeEnabled
|
|
2612
|
+
} : void 0
|
|
2613
|
+
});
|
|
2614
|
+
} catch (error) {
|
|
2615
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2616
|
+
program.error(import_picocolors.default.red(`Failed to initialize debug diagnostics: ${message}`));
|
|
2216
2617
|
return;
|
|
2217
2618
|
}
|
|
2218
|
-
|
|
2619
|
+
try {
|
|
2620
|
+
const resolved = resolveCountRunOptions(options);
|
|
2621
|
+
if (hasPathInput(options.path)) {
|
|
2622
|
+
await executeBatchCount({
|
|
2623
|
+
argv,
|
|
2624
|
+
options,
|
|
2625
|
+
runtime,
|
|
2626
|
+
resolved,
|
|
2627
|
+
debug,
|
|
2628
|
+
teeEnabled
|
|
2629
|
+
});
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
await executeSingleCount({
|
|
2633
|
+
textTokens,
|
|
2634
|
+
options,
|
|
2635
|
+
resolved
|
|
2636
|
+
});
|
|
2637
|
+
} catch (error) {
|
|
2638
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2639
|
+
if (message === "No input provided. Pass text, pipe stdin, or use --path.") {
|
|
2640
|
+
program.error(import_picocolors.default.red(message));
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2643
|
+
if (message === "No readable text-like inputs were found from --path.") {
|
|
2644
|
+
program.error(import_picocolors.default.red(message));
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
program.error(message);
|
|
2648
|
+
} finally {
|
|
2649
|
+
await debug.close();
|
|
2650
|
+
}
|
|
2219
2651
|
});
|
|
2220
2652
|
await program.parseAsync(argv);
|
|
2221
2653
|
}
|