@dev-pi2pie/word-counter 0.1.5-canary.4 → 0.1.5
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 +60 -5
- package/dist/esm/bin.mjs +1010 -136
- package/dist/esm/bin.mjs.map +1 -1
- package/dist/esm/worker/count-worker.mjs +1 -1
- package/dist/esm/worker/count-worker.mjs.map +1 -1
- package/dist/esm/worker-pool.mjs +1 -0
- package/dist/esm/worker-pool.mjs.map +1 -1
- package/dist/wasm-language-detector/language_detector_bg.wasm +0 -0
- package/package.json +7 -7
package/dist/esm/bin.mjs
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { Command, Option } from "commander";
|
|
5
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { basename, dirname, extname, join, relative, resolve, sep, win32 } from "node:path";
|
|
5
7
|
import { closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, statSync } from "node:fs";
|
|
6
|
-
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
7
8
|
import os from "node:os";
|
|
8
9
|
import { parseDocument } from "yaml";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { readFile, readdir, stat } from "node:fs/promises";
|
|
11
11
|
//#region \0rolldown/runtime.js
|
|
12
12
|
var __create = Object.create;
|
|
13
13
|
var __defProp = Object.defineProperty;
|
|
@@ -31,6 +31,884 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
enumerable: true
|
|
32
32
|
}) : target, mod));
|
|
33
33
|
//#endregion
|
|
34
|
+
//#region src/cli/config/schema.ts
|
|
35
|
+
const CONFIG_FILE_BASENAME = "wc-intl-seg.config";
|
|
36
|
+
const CONFIG_FORMAT_PRIORITY = [
|
|
37
|
+
"toml",
|
|
38
|
+
"jsonc",
|
|
39
|
+
"json"
|
|
40
|
+
];
|
|
41
|
+
CONFIG_FORMAT_PRIORITY.map((format) => `${CONFIG_FILE_BASENAME}.${format}`);
|
|
42
|
+
const CONFIG_DETECTOR_VALUES = ["regex", "wasm"];
|
|
43
|
+
const CONFIG_CONTENT_GATE_MODE_VALUES = [
|
|
44
|
+
"default",
|
|
45
|
+
"strict",
|
|
46
|
+
"loose",
|
|
47
|
+
"off"
|
|
48
|
+
];
|
|
49
|
+
const CONFIG_PATH_MODE_VALUES = ["auto", "manual"];
|
|
50
|
+
const CONFIG_PROGRESS_MODE_VALUES = [
|
|
51
|
+
"auto",
|
|
52
|
+
"on",
|
|
53
|
+
"off"
|
|
54
|
+
];
|
|
55
|
+
const CONFIG_LOG_LEVEL_VALUES = ["info", "debug"];
|
|
56
|
+
const CONFIG_LOG_VERBOSITY_VALUES = ["compact", "verbose"];
|
|
57
|
+
const CONFIG_TOTAL_OF_VALUES = [
|
|
58
|
+
"words",
|
|
59
|
+
"emoji",
|
|
60
|
+
"symbols",
|
|
61
|
+
"punctuation",
|
|
62
|
+
"whitespace"
|
|
63
|
+
];
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/cli/config/discover.ts
|
|
66
|
+
async function fileExists(path) {
|
|
67
|
+
try {
|
|
68
|
+
return (await stat(path)).isFile();
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function createIgnoredSiblingNote(scope, selectedPath, ignoredSiblingPaths) {
|
|
74
|
+
if (ignoredSiblingPaths.length === 0) return;
|
|
75
|
+
return [`Using ${scope} config file "${selectedPath}".`, `Ignoring lower-priority sibling config files: ${ignoredSiblingPaths.join(", ")}.`].join(" ");
|
|
76
|
+
}
|
|
77
|
+
function resolveUserConfigDirectory(options = {}) {
|
|
78
|
+
return resolveUserConfigDirectories(options)[0]?.directory;
|
|
79
|
+
}
|
|
80
|
+
function resolveUserConfigDirectories(options = {}) {
|
|
81
|
+
const platform = options.platform ?? process.platform;
|
|
82
|
+
const env = options.env ?? process.env;
|
|
83
|
+
const homeDir = env.HOME ?? env.USERPROFILE;
|
|
84
|
+
if (platform === "win32") {
|
|
85
|
+
const candidates = [];
|
|
86
|
+
if (homeDir) candidates.push({
|
|
87
|
+
directory: win32.join(homeDir, ".config"),
|
|
88
|
+
kind: "primary"
|
|
89
|
+
});
|
|
90
|
+
if (env.APPDATA) candidates.push({
|
|
91
|
+
directory: env.APPDATA,
|
|
92
|
+
kind: "legacy-windows"
|
|
93
|
+
});
|
|
94
|
+
return candidates;
|
|
95
|
+
}
|
|
96
|
+
if (platform === "darwin") {
|
|
97
|
+
const candidates = [];
|
|
98
|
+
if (env.XDG_CONFIG_HOME) candidates.push({
|
|
99
|
+
directory: env.XDG_CONFIG_HOME,
|
|
100
|
+
kind: "primary"
|
|
101
|
+
});
|
|
102
|
+
if (homeDir) {
|
|
103
|
+
candidates.push({
|
|
104
|
+
directory: join(homeDir, ".config"),
|
|
105
|
+
kind: "primary"
|
|
106
|
+
});
|
|
107
|
+
candidates.push({
|
|
108
|
+
directory: join(homeDir, "Library", "Application Support"),
|
|
109
|
+
kind: "legacy-macos"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return candidates;
|
|
113
|
+
}
|
|
114
|
+
const directory = env.XDG_CONFIG_HOME ?? (homeDir ? join(homeDir, ".config") : void 0);
|
|
115
|
+
return directory ? [{
|
|
116
|
+
directory,
|
|
117
|
+
kind: "primary"
|
|
118
|
+
}] : [];
|
|
119
|
+
}
|
|
120
|
+
async function discoverConfigFileInDirectory(directory, scope) {
|
|
121
|
+
const existing = [];
|
|
122
|
+
for (const format of CONFIG_FORMAT_PRIORITY) {
|
|
123
|
+
const path = join(directory, `${CONFIG_FILE_BASENAME}.${format}`);
|
|
124
|
+
if (await fileExists(path)) existing.push({
|
|
125
|
+
format,
|
|
126
|
+
path
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (existing.length === 0) return;
|
|
130
|
+
const selected = existing[0];
|
|
131
|
+
const ignoredSiblingPaths = existing.slice(1).map((item) => item.path);
|
|
132
|
+
const note = createIgnoredSiblingNote(scope, selected.path, ignoredSiblingPaths);
|
|
133
|
+
return {
|
|
134
|
+
scope,
|
|
135
|
+
directory,
|
|
136
|
+
path: selected.path,
|
|
137
|
+
format: selected.format,
|
|
138
|
+
ignoredSiblingPaths,
|
|
139
|
+
notes: note ? [note] : []
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async function discoverConfigFiles(options = {}) {
|
|
143
|
+
const cwd = options.cwd ?? process.cwd();
|
|
144
|
+
const userDirectories = resolveUserConfigDirectories(options);
|
|
145
|
+
const currentWorkingDirectory = await discoverConfigFileInDirectory(cwd, "cwd");
|
|
146
|
+
let user;
|
|
147
|
+
const discoveredFallbackUsers = [];
|
|
148
|
+
for (const candidate of userDirectories) {
|
|
149
|
+
const discovered = await discoverConfigFileInDirectory(candidate.directory, "user");
|
|
150
|
+
if (!discovered) continue;
|
|
151
|
+
if (!user) {
|
|
152
|
+
user = discovered;
|
|
153
|
+
if (candidate.kind === "legacy-macos") user.notes.push(`Using legacy macOS user config location "${discovered.directory}". Migrate this config to "${resolveUserConfigDirectory(options)}" to follow the current default path.`);
|
|
154
|
+
if (candidate.kind === "legacy-windows") user.notes.push(`Using legacy Windows user config location "${discovered.directory}". Migrate this config to "${resolveUserConfigDirectory(options)}" to follow the current default path.`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
discoveredFallbackUsers.push(discovered);
|
|
158
|
+
}
|
|
159
|
+
if (user) for (const fallback of discoveredFallbackUsers) user.notes.push(`Ignoring fallback user config file "${fallback.path}" because a higher-priority user config file was found at "${user.path}".`);
|
|
160
|
+
return {
|
|
161
|
+
...user ? { user } : {},
|
|
162
|
+
...currentWorkingDirectory ? { cwd: currentWorkingDirectory } : {}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/cli/config/apply.ts
|
|
167
|
+
function withConfigQuietSkips(currentQuietSkips, skippedFiles) {
|
|
168
|
+
if (skippedFiles === void 0) return currentQuietSkips;
|
|
169
|
+
return skippedFiles ? false : true;
|
|
170
|
+
}
|
|
171
|
+
function applyConfigToCountOptions(options, config, sources) {
|
|
172
|
+
const next = { ...options };
|
|
173
|
+
if (!sources.detector && config.detector !== void 0) next.detector = config.detector;
|
|
174
|
+
if (!sources.contentGate && config.contentGate?.mode !== void 0) next.contentGate = config.contentGate.mode;
|
|
175
|
+
if (!sources.pathMode && config.path?.mode !== void 0) next.pathMode = config.path.mode;
|
|
176
|
+
if (!sources.recursive && config.path?.recursive !== void 0) next.recursive = config.path.recursive;
|
|
177
|
+
next.pathDetectBinary = config.path?.detectBinary ?? next.pathDetectBinary ?? true;
|
|
178
|
+
if (!sources.includeExt && config.path?.includeExtensions !== void 0) next.includeExt = [...config.path.includeExtensions];
|
|
179
|
+
if (!sources.excludeExt && config.path?.excludeExtensions !== void 0) next.excludeExt = [...config.path.excludeExtensions];
|
|
180
|
+
if (!sources.totalOf && config.output?.totalOf !== void 0) next.totalOf = [...config.output.totalOf];
|
|
181
|
+
if (!sources.debug && config.logging?.level !== void 0) next.debug = config.logging.level === "debug";
|
|
182
|
+
const debugEnabled = next.debug === true;
|
|
183
|
+
if (debugEnabled && !sources.verbose && config.logging?.verbosity !== void 0) next.verbose = config.logging.verbosity === "verbose";
|
|
184
|
+
if (debugEnabled && !sources.debugReport && config.reporting?.debugReport?.path !== void 0) next.debugReport = config.reporting.debugReport.path;
|
|
185
|
+
const debugReportEnabled = next.debugReport !== void 0 && next.debugReport !== false;
|
|
186
|
+
if (debugEnabled && debugReportEnabled && !sources.debugReportTee && config.reporting?.debugReport?.tee !== void 0) next.debugReportTee = config.reporting.debugReport.tee;
|
|
187
|
+
if (!sources.progress && config.progress?.mode !== void 0) next.progressMode = config.progress.mode;
|
|
188
|
+
if (!sources.quietSkips) next.quietSkips = withConfigQuietSkips(next.quietSkips, config.reporting?.skippedFiles);
|
|
189
|
+
return next;
|
|
190
|
+
}
|
|
191
|
+
function applyConfigToInspectInvocation(validated, config) {
|
|
192
|
+
const next = {
|
|
193
|
+
...validated,
|
|
194
|
+
includeExt: [...validated.includeExt],
|
|
195
|
+
excludeExt: [...validated.excludeExt],
|
|
196
|
+
sources: { ...validated.sources }
|
|
197
|
+
};
|
|
198
|
+
const detectorFromConfig = config.inspect?.detector ?? config.detector;
|
|
199
|
+
if (!next.sources.detector && detectorFromConfig !== void 0) next.detector = detectorFromConfig;
|
|
200
|
+
const contentGateFromConfig = config.inspect?.contentGate?.mode ?? config.contentGate?.mode;
|
|
201
|
+
if (!next.sources.contentGate && contentGateFromConfig !== void 0) next.contentGateMode = contentGateFromConfig;
|
|
202
|
+
if (!next.sources.pathMode && config.path?.mode !== void 0) next.pathMode = config.path.mode;
|
|
203
|
+
if (!next.sources.recursive && config.path?.recursive !== void 0) next.recursive = config.path.recursive;
|
|
204
|
+
next.pathDetectBinary = config.path?.detectBinary ?? next.pathDetectBinary ?? true;
|
|
205
|
+
if (!next.sources.includeExt && config.path?.includeExtensions !== void 0) next.includeExt = [...config.path.includeExtensions];
|
|
206
|
+
if (!next.sources.excludeExt && config.path?.excludeExtensions !== void 0) next.excludeExt = [...config.path.excludeExtensions];
|
|
207
|
+
return next;
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/cli/total-of.ts
|
|
211
|
+
const TOTAL_OF_PARTS = Object.freeze([
|
|
212
|
+
"words",
|
|
213
|
+
"emoji",
|
|
214
|
+
"symbols",
|
|
215
|
+
"punctuation",
|
|
216
|
+
"whitespace"
|
|
217
|
+
]);
|
|
218
|
+
const TOTAL_OF_PART_ALIASES = {
|
|
219
|
+
word: "words",
|
|
220
|
+
words: "words",
|
|
221
|
+
emoji: "emoji",
|
|
222
|
+
emojis: "emoji",
|
|
223
|
+
symbol: "symbols",
|
|
224
|
+
symbols: "symbols",
|
|
225
|
+
punction: "punctuation",
|
|
226
|
+
punctuation: "punctuation",
|
|
227
|
+
whitespace: "whitespace"
|
|
228
|
+
};
|
|
229
|
+
function createTotalOfCounts() {
|
|
230
|
+
return {
|
|
231
|
+
words: 0,
|
|
232
|
+
emoji: 0,
|
|
233
|
+
symbols: 0,
|
|
234
|
+
punctuation: 0,
|
|
235
|
+
whitespace: 0
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function collectNonWordCounts(target, nonWords) {
|
|
239
|
+
if (!nonWords) return;
|
|
240
|
+
target.emoji += nonWords.counts.emoji;
|
|
241
|
+
target.symbols += nonWords.counts.symbols;
|
|
242
|
+
target.punctuation += nonWords.counts.punctuation;
|
|
243
|
+
target.whitespace += nonWords.counts.whitespace ?? 0;
|
|
244
|
+
}
|
|
245
|
+
function collectFromWordCounterResult(result) {
|
|
246
|
+
const counts = createTotalOfCounts();
|
|
247
|
+
counts.words += result.counts?.words ?? result.total;
|
|
248
|
+
if (result.breakdown.mode === "collector") {
|
|
249
|
+
collectNonWordCounts(counts, result.breakdown.nonWords);
|
|
250
|
+
return counts;
|
|
251
|
+
}
|
|
252
|
+
for (const item of result.breakdown.items) collectNonWordCounts(counts, item.nonWords);
|
|
253
|
+
return counts;
|
|
254
|
+
}
|
|
255
|
+
function collectTotalOfCounts(result) {
|
|
256
|
+
if (!("section" in result)) return collectFromWordCounterResult(result);
|
|
257
|
+
const counts = createTotalOfCounts();
|
|
258
|
+
for (const item of result.items) {
|
|
259
|
+
const itemCounts = collectFromWordCounterResult(item.result);
|
|
260
|
+
for (const part of TOTAL_OF_PARTS) counts[part] += itemCounts[part];
|
|
261
|
+
}
|
|
262
|
+
return counts;
|
|
263
|
+
}
|
|
264
|
+
function parseTotalOfToken(token) {
|
|
265
|
+
const canonical = TOTAL_OF_PART_ALIASES[token.trim().toLowerCase()];
|
|
266
|
+
if (canonical) return canonical;
|
|
267
|
+
throw new Error(`Invalid --total-of part: ${token}. Allowed: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
268
|
+
}
|
|
269
|
+
function parseTotalOfOption(value) {
|
|
270
|
+
const rawTokens = value.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
271
|
+
if (rawTokens.length === 0) throw new Error(`Invalid --total-of value: "${value}". Use comma-separated parts from: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
272
|
+
const parts = [];
|
|
273
|
+
const seen = /* @__PURE__ */ new Set();
|
|
274
|
+
for (const token of rawTokens) {
|
|
275
|
+
const parsed = parseTotalOfToken(token);
|
|
276
|
+
if (seen.has(parsed)) continue;
|
|
277
|
+
seen.add(parsed);
|
|
278
|
+
parts.push(parsed);
|
|
279
|
+
}
|
|
280
|
+
return parts;
|
|
281
|
+
}
|
|
282
|
+
function requiresNonWordCollection(parts) {
|
|
283
|
+
if (!parts || parts.length === 0) return false;
|
|
284
|
+
return parts.some((part) => part !== "words");
|
|
285
|
+
}
|
|
286
|
+
function requiresWhitespaceCollection(parts) {
|
|
287
|
+
if (!parts || parts.length === 0) return false;
|
|
288
|
+
return parts.includes("whitespace");
|
|
289
|
+
}
|
|
290
|
+
function resolveTotalOfOverride(result, parts) {
|
|
291
|
+
if (!parts || parts.length === 0) return;
|
|
292
|
+
const counts = collectTotalOfCounts(result);
|
|
293
|
+
let total = 0;
|
|
294
|
+
for (const part of parts) total += counts[part];
|
|
295
|
+
return {
|
|
296
|
+
parts: [...parts],
|
|
297
|
+
total
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function formatTotalOfParts(parts) {
|
|
301
|
+
return parts.join(", ");
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region src/cli/config/errors.ts
|
|
305
|
+
var ConfigValidationError = class extends Error {
|
|
306
|
+
sourceLabel;
|
|
307
|
+
path;
|
|
308
|
+
constructor(sourceLabel, path, message) {
|
|
309
|
+
const suffix = path.length > 0 ? ` at "${path.join(".")}"` : "";
|
|
310
|
+
super(`Invalid config in ${sourceLabel}${suffix}: ${message}`);
|
|
311
|
+
this.name = "ConfigValidationError";
|
|
312
|
+
this.sourceLabel = sourceLabel;
|
|
313
|
+
this.path = [...path];
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
var ConfigParseError = class extends Error {
|
|
317
|
+
sourceLabel;
|
|
318
|
+
format;
|
|
319
|
+
constructor(sourceLabel, format, message) {
|
|
320
|
+
super(`Invalid ${format.toUpperCase()} config in ${sourceLabel}: ${message}`);
|
|
321
|
+
this.name = "ConfigParseError";
|
|
322
|
+
this.sourceLabel = sourceLabel;
|
|
323
|
+
this.format = format;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/cli/config/normalize.ts
|
|
328
|
+
function isRecord$1(value) {
|
|
329
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
330
|
+
}
|
|
331
|
+
function createConfigError(sourceLabel, path, message) {
|
|
332
|
+
return new ConfigValidationError(sourceLabel, path, message);
|
|
333
|
+
}
|
|
334
|
+
function ensureObject$1(value, sourceLabel, path) {
|
|
335
|
+
if (value === void 0) return;
|
|
336
|
+
if (!isRecord$1(value)) throw createConfigError(sourceLabel, path, "expected an object.");
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
function rejectUnknownKeys(value, allowedKeys, sourceLabel, path) {
|
|
340
|
+
for (const key of Object.keys(value)) if (!allowedKeys.includes(key)) throw createConfigError(sourceLabel, [...path, key], "unknown key.");
|
|
341
|
+
}
|
|
342
|
+
function parseBoolean(value, sourceLabel, path) {
|
|
343
|
+
if (value === void 0) return;
|
|
344
|
+
if (typeof value !== "boolean") throw createConfigError(sourceLabel, path, "expected a boolean.");
|
|
345
|
+
return value;
|
|
346
|
+
}
|
|
347
|
+
function parseString(value, sourceLabel, path) {
|
|
348
|
+
if (value === void 0) return;
|
|
349
|
+
if (typeof value !== "string") throw createConfigError(sourceLabel, path, "expected a string.");
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
352
|
+
function parseStringArray(value, sourceLabel, path) {
|
|
353
|
+
if (value === void 0) return;
|
|
354
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw createConfigError(sourceLabel, path, "expected an array of strings.");
|
|
355
|
+
return [...value];
|
|
356
|
+
}
|
|
357
|
+
function parseEnum(value, allowedValues, sourceLabel, path) {
|
|
358
|
+
if (value === void 0) return;
|
|
359
|
+
if (typeof value !== "string" || !allowedValues.includes(value)) throw createConfigError(sourceLabel, path, `expected one of: ${allowedValues.map((item) => `"${item}"`).join(", ")}.`);
|
|
360
|
+
return value;
|
|
361
|
+
}
|
|
362
|
+
function parseTotalOf(value, sourceLabel, path) {
|
|
363
|
+
if (value === void 0) return;
|
|
364
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw createConfigError(sourceLabel, path, "expected an array of strings.");
|
|
365
|
+
const parts = [];
|
|
366
|
+
for (const item of value) {
|
|
367
|
+
const parsed = parseEnum(item, CONFIG_TOTAL_OF_VALUES, sourceLabel, path);
|
|
368
|
+
if (parsed && !parts.includes(parsed)) parts.push(parsed);
|
|
369
|
+
}
|
|
370
|
+
return parts;
|
|
371
|
+
}
|
|
372
|
+
function normalizeInspectConfig(value, sourceLabel) {
|
|
373
|
+
const section = ensureObject$1(value, sourceLabel, ["inspect"]);
|
|
374
|
+
if (!section) return;
|
|
375
|
+
rejectUnknownKeys(section, ["detector", "contentGate"], sourceLabel, ["inspect"]);
|
|
376
|
+
const detector = parseEnum(section.detector, CONFIG_DETECTOR_VALUES, sourceLabel, ["inspect", "detector"]);
|
|
377
|
+
const contentGateSection = ensureObject$1(section.contentGate, sourceLabel, ["inspect", "contentGate"]);
|
|
378
|
+
if (contentGateSection) rejectUnknownKeys(contentGateSection, ["mode"], sourceLabel, ["inspect", "contentGate"]);
|
|
379
|
+
const mode = parseEnum(contentGateSection?.mode, CONFIG_CONTENT_GATE_MODE_VALUES, sourceLabel, [
|
|
380
|
+
"inspect",
|
|
381
|
+
"contentGate",
|
|
382
|
+
"mode"
|
|
383
|
+
]);
|
|
384
|
+
const normalized = {};
|
|
385
|
+
if (detector !== void 0) normalized.detector = detector;
|
|
386
|
+
if (mode !== void 0) normalized.contentGate = { mode };
|
|
387
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
388
|
+
}
|
|
389
|
+
function normalizeContentGateConfig(value, sourceLabel) {
|
|
390
|
+
const section = ensureObject$1(value, sourceLabel, ["contentGate"]);
|
|
391
|
+
if (!section) return;
|
|
392
|
+
rejectUnknownKeys(section, ["mode"], sourceLabel, ["contentGate"]);
|
|
393
|
+
const mode = parseEnum(section.mode, CONFIG_CONTENT_GATE_MODE_VALUES, sourceLabel, ["contentGate", "mode"]);
|
|
394
|
+
return mode === void 0 ? void 0 : { mode };
|
|
395
|
+
}
|
|
396
|
+
function normalizePathConfig(value, sourceLabel) {
|
|
397
|
+
const section = ensureObject$1(value, sourceLabel, ["path"]);
|
|
398
|
+
if (!section) return;
|
|
399
|
+
rejectUnknownKeys(section, [
|
|
400
|
+
"mode",
|
|
401
|
+
"recursive",
|
|
402
|
+
"includeExtensions",
|
|
403
|
+
"excludeExtensions",
|
|
404
|
+
"detectBinary"
|
|
405
|
+
], sourceLabel, ["path"]);
|
|
406
|
+
const mode = parseEnum(section.mode, CONFIG_PATH_MODE_VALUES, sourceLabel, ["path", "mode"]);
|
|
407
|
+
const recursive = parseBoolean(section.recursive, sourceLabel, ["path", "recursive"]);
|
|
408
|
+
const includeExtensions = parseStringArray(section.includeExtensions, sourceLabel, ["path", "includeExtensions"]);
|
|
409
|
+
const excludeExtensions = parseStringArray(section.excludeExtensions, sourceLabel, ["path", "excludeExtensions"]);
|
|
410
|
+
const detectBinary = parseBoolean(section.detectBinary, sourceLabel, ["path", "detectBinary"]);
|
|
411
|
+
const normalized = {};
|
|
412
|
+
if (mode !== void 0) normalized.mode = mode;
|
|
413
|
+
if (recursive !== void 0) normalized.recursive = recursive;
|
|
414
|
+
if (includeExtensions !== void 0) normalized.includeExtensions = includeExtensions;
|
|
415
|
+
if (excludeExtensions !== void 0) normalized.excludeExtensions = excludeExtensions;
|
|
416
|
+
if (detectBinary !== void 0) normalized.detectBinary = detectBinary;
|
|
417
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
418
|
+
}
|
|
419
|
+
function normalizeProgressConfig(value, sourceLabel) {
|
|
420
|
+
const section = ensureObject$1(value, sourceLabel, ["progress"]);
|
|
421
|
+
if (!section) return;
|
|
422
|
+
rejectUnknownKeys(section, ["mode"], sourceLabel, ["progress"]);
|
|
423
|
+
const mode = parseEnum(section.mode, CONFIG_PROGRESS_MODE_VALUES, sourceLabel, ["progress", "mode"]);
|
|
424
|
+
return mode === void 0 ? void 0 : { mode };
|
|
425
|
+
}
|
|
426
|
+
function normalizeOutputConfig(value, sourceLabel) {
|
|
427
|
+
const section = ensureObject$1(value, sourceLabel, ["output"]);
|
|
428
|
+
if (!section) return;
|
|
429
|
+
rejectUnknownKeys(section, ["totalOf"], sourceLabel, ["output"]);
|
|
430
|
+
const totalOf = parseTotalOf(section.totalOf, sourceLabel, ["output", "totalOf"]);
|
|
431
|
+
return totalOf === void 0 ? void 0 : { totalOf };
|
|
432
|
+
}
|
|
433
|
+
function normalizeReportingConfig(value, sourceLabel) {
|
|
434
|
+
const section = ensureObject$1(value, sourceLabel, ["reporting"]);
|
|
435
|
+
if (!section) return;
|
|
436
|
+
rejectUnknownKeys(section, ["skippedFiles", "debugReport"], sourceLabel, ["reporting"]);
|
|
437
|
+
const skippedFiles = parseBoolean(section.skippedFiles, sourceLabel, ["reporting", "skippedFiles"]);
|
|
438
|
+
const debugReportSection = ensureObject$1(section.debugReport, sourceLabel, ["reporting", "debugReport"]);
|
|
439
|
+
if (debugReportSection) rejectUnknownKeys(debugReportSection, ["path", "tee"], sourceLabel, ["reporting", "debugReport"]);
|
|
440
|
+
const path = parseString(debugReportSection?.path, sourceLabel, [
|
|
441
|
+
"reporting",
|
|
442
|
+
"debugReport",
|
|
443
|
+
"path"
|
|
444
|
+
]);
|
|
445
|
+
const tee = parseBoolean(debugReportSection?.tee, sourceLabel, [
|
|
446
|
+
"reporting",
|
|
447
|
+
"debugReport",
|
|
448
|
+
"tee"
|
|
449
|
+
]);
|
|
450
|
+
const normalized = {};
|
|
451
|
+
if (skippedFiles !== void 0) normalized.skippedFiles = skippedFiles;
|
|
452
|
+
if (path !== void 0 || tee !== void 0) {
|
|
453
|
+
normalized.debugReport = {};
|
|
454
|
+
if (path !== void 0) normalized.debugReport.path = path;
|
|
455
|
+
if (tee !== void 0) normalized.debugReport.tee = tee;
|
|
456
|
+
}
|
|
457
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
458
|
+
}
|
|
459
|
+
function normalizeLoggingConfig(value, sourceLabel) {
|
|
460
|
+
const section = ensureObject$1(value, sourceLabel, ["logging"]);
|
|
461
|
+
if (!section) return;
|
|
462
|
+
rejectUnknownKeys(section, ["level", "verbosity"], sourceLabel, ["logging"]);
|
|
463
|
+
const level = parseEnum(section.level, CONFIG_LOG_LEVEL_VALUES, sourceLabel, ["logging", "level"]);
|
|
464
|
+
const verbosity = parseEnum(section.verbosity, CONFIG_LOG_VERBOSITY_VALUES, sourceLabel, ["logging", "verbosity"]);
|
|
465
|
+
const normalized = {};
|
|
466
|
+
if (level !== void 0) normalized.level = level;
|
|
467
|
+
if (verbosity !== void 0) normalized.verbosity = verbosity;
|
|
468
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
469
|
+
}
|
|
470
|
+
function normalizeWordCounterConfig(value, sourceLabel) {
|
|
471
|
+
const root = ensureObject$1(value, sourceLabel, []);
|
|
472
|
+
if (!root) throw createConfigError(sourceLabel, [], "expected a root object.");
|
|
473
|
+
rejectUnknownKeys(root, [
|
|
474
|
+
"detector",
|
|
475
|
+
"contentGate",
|
|
476
|
+
"inspect",
|
|
477
|
+
"path",
|
|
478
|
+
"progress",
|
|
479
|
+
"output",
|
|
480
|
+
"reporting",
|
|
481
|
+
"logging"
|
|
482
|
+
], sourceLabel, []);
|
|
483
|
+
const detector = parseEnum(root.detector, CONFIG_DETECTOR_VALUES, sourceLabel, ["detector"]);
|
|
484
|
+
const contentGate = normalizeContentGateConfig(root.contentGate, sourceLabel);
|
|
485
|
+
const inspect = normalizeInspectConfig(root.inspect, sourceLabel);
|
|
486
|
+
const path = normalizePathConfig(root.path, sourceLabel);
|
|
487
|
+
const progress = normalizeProgressConfig(root.progress, sourceLabel);
|
|
488
|
+
const output = normalizeOutputConfig(root.output, sourceLabel);
|
|
489
|
+
const reporting = normalizeReportingConfig(root.reporting, sourceLabel);
|
|
490
|
+
const logging = normalizeLoggingConfig(root.logging, sourceLabel);
|
|
491
|
+
const normalized = {};
|
|
492
|
+
if (detector !== void 0) normalized.detector = detector;
|
|
493
|
+
if (contentGate !== void 0) normalized.contentGate = contentGate;
|
|
494
|
+
if (inspect !== void 0) normalized.inspect = inspect;
|
|
495
|
+
if (path !== void 0) normalized.path = path;
|
|
496
|
+
if (progress !== void 0) normalized.progress = progress;
|
|
497
|
+
if (output !== void 0) normalized.output = output;
|
|
498
|
+
if (reporting !== void 0) normalized.reporting = reporting;
|
|
499
|
+
if (logging !== void 0) normalized.logging = logging;
|
|
500
|
+
return normalized;
|
|
501
|
+
}
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region src/cli/config/env.ts
|
|
504
|
+
function parseBooleanEnv(name, value) {
|
|
505
|
+
if (value === void 0) return;
|
|
506
|
+
const normalized = value.trim().toLowerCase();
|
|
507
|
+
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") return true;
|
|
508
|
+
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") return false;
|
|
509
|
+
throw new Error(`Invalid value for ${name}: ${value}`);
|
|
510
|
+
}
|
|
511
|
+
function parseCommaSeparatedEnv(value) {
|
|
512
|
+
if (value === void 0) return;
|
|
513
|
+
const tokens = value.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
514
|
+
return tokens.length > 0 ? tokens : [];
|
|
515
|
+
}
|
|
516
|
+
function resolveEnvConfig(env = process.env) {
|
|
517
|
+
const pathMode = env.WORD_COUNTER_PATH_MODE;
|
|
518
|
+
const recursive = parseBooleanEnv("WORD_COUNTER_RECURSIVE", env.WORD_COUNTER_RECURSIVE);
|
|
519
|
+
const includeExtensions = parseCommaSeparatedEnv(env.WORD_COUNTER_INCLUDE_EXT);
|
|
520
|
+
const excludeExtensions = parseCommaSeparatedEnv(env.WORD_COUNTER_EXCLUDE_EXT);
|
|
521
|
+
const skippedFiles = parseBooleanEnv("WORD_COUNTER_REPORT_SKIPS", env.WORD_COUNTER_REPORT_SKIPS);
|
|
522
|
+
const totalOfRaw = env.WORD_COUNTER_TOTAL_OF;
|
|
523
|
+
const contentGateMode = env.WORD_COUNTER_CONTENT_GATE;
|
|
524
|
+
const progressMode = env.WORD_COUNTER_PROGRESS;
|
|
525
|
+
const logLevel = env.WORD_COUNTER_LOG_LEVEL;
|
|
526
|
+
const logVerbosity = env.WORD_COUNTER_LOG_VERBOSITY;
|
|
527
|
+
const debugReportPath = env.WORD_COUNTER_DEBUG_REPORT;
|
|
528
|
+
const debugReportTee = parseBooleanEnv("WORD_COUNTER_DEBUG_REPORT_TEE", env.WORD_COUNTER_DEBUG_REPORT_TEE);
|
|
529
|
+
const config = {};
|
|
530
|
+
if (pathMode !== void 0 || recursive !== void 0 || includeExtensions !== void 0 || excludeExtensions !== void 0) {
|
|
531
|
+
config.path = {};
|
|
532
|
+
if (pathMode !== void 0) config.path.mode = pathMode;
|
|
533
|
+
if (recursive !== void 0) config.path.recursive = recursive;
|
|
534
|
+
if (includeExtensions !== void 0) config.path.includeExtensions = includeExtensions;
|
|
535
|
+
if (excludeExtensions !== void 0) config.path.excludeExtensions = excludeExtensions;
|
|
536
|
+
}
|
|
537
|
+
if (progressMode !== void 0) config.progress = { mode: progressMode };
|
|
538
|
+
if (totalOfRaw !== void 0) config.output = { totalOf: parseTotalOfOption(totalOfRaw) };
|
|
539
|
+
if (contentGateMode !== void 0) {
|
|
540
|
+
config.contentGate = { mode: contentGateMode };
|
|
541
|
+
config.inspect = {
|
|
542
|
+
...config.inspect,
|
|
543
|
+
contentGate: { mode: contentGateMode }
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (skippedFiles !== void 0 || debugReportPath !== void 0 || debugReportTee !== void 0) {
|
|
547
|
+
config.reporting = {};
|
|
548
|
+
if (skippedFiles !== void 0) config.reporting.skippedFiles = skippedFiles;
|
|
549
|
+
if (debugReportPath !== void 0 || debugReportTee !== void 0) {
|
|
550
|
+
config.reporting.debugReport = {};
|
|
551
|
+
if (debugReportPath !== void 0) config.reporting.debugReport.path = debugReportPath;
|
|
552
|
+
if (debugReportTee !== void 0) config.reporting.debugReport.tee = debugReportTee;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (logLevel !== void 0 || logVerbosity !== void 0) {
|
|
556
|
+
config.logging = {};
|
|
557
|
+
if (logLevel !== void 0) config.logging.level = logLevel;
|
|
558
|
+
if (logVerbosity !== void 0) config.logging.verbosity = logVerbosity;
|
|
559
|
+
}
|
|
560
|
+
return normalizeWordCounterConfig(config, "environment variables");
|
|
561
|
+
}
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/cli/config/merge.ts
|
|
564
|
+
function mergeSection(base, override) {
|
|
565
|
+
if (!base) return override ? { ...override } : void 0;
|
|
566
|
+
if (!override) return { ...base };
|
|
567
|
+
return {
|
|
568
|
+
...base,
|
|
569
|
+
...override
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function mergeNestedSection(base, override, nestedKey) {
|
|
573
|
+
const merged = mergeSection(base, override);
|
|
574
|
+
if (!merged) return;
|
|
575
|
+
const baseRecord = base;
|
|
576
|
+
const overrideRecord = override;
|
|
577
|
+
const baseNested = baseRecord?.[nestedKey];
|
|
578
|
+
const overrideNested = overrideRecord?.[nestedKey];
|
|
579
|
+
if (typeof baseNested === "object" && baseNested !== null && typeof overrideNested === "object" && overrideNested !== null) return {
|
|
580
|
+
...merged,
|
|
581
|
+
[nestedKey]: {
|
|
582
|
+
...baseNested,
|
|
583
|
+
...overrideNested
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
return merged;
|
|
587
|
+
}
|
|
588
|
+
function mergeWordCounterConfig(base, override) {
|
|
589
|
+
return {
|
|
590
|
+
...base,
|
|
591
|
+
...override,
|
|
592
|
+
...base.inspect || override.inspect ? { inspect: mergeSection(base.inspect, override.inspect) } : {},
|
|
593
|
+
...base.path || override.path ? { path: mergeSection(base.path, override.path) } : {},
|
|
594
|
+
...base.progress || override.progress ? { progress: mergeSection(base.progress, override.progress) } : {},
|
|
595
|
+
...base.output || override.output ? { output: mergeSection(base.output, override.output) } : {},
|
|
596
|
+
...base.reporting || override.reporting ? { reporting: mergeNestedSection(base.reporting, override.reporting, "debugReport") } : {},
|
|
597
|
+
...base.logging || override.logging ? { logging: mergeSection(base.logging, override.logging) } : {}
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
//#endregion
|
|
601
|
+
//#region src/cli/config/jsonc.ts
|
|
602
|
+
function isEscaped(text, index) {
|
|
603
|
+
let backslashCount = 0;
|
|
604
|
+
for (let cursor = index - 1; cursor >= 0 && text[cursor] === "\\"; cursor -= 1) backslashCount += 1;
|
|
605
|
+
return backslashCount % 2 === 1;
|
|
606
|
+
}
|
|
607
|
+
function stripJsonComments(text) {
|
|
608
|
+
let result = "";
|
|
609
|
+
let inString = false;
|
|
610
|
+
let inLineComment = false;
|
|
611
|
+
let inBlockComment = false;
|
|
612
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
613
|
+
const current = text[index] ?? "";
|
|
614
|
+
const next = text[index + 1] ?? "";
|
|
615
|
+
if (inLineComment) {
|
|
616
|
+
if (current === "\n") {
|
|
617
|
+
inLineComment = false;
|
|
618
|
+
result += current;
|
|
619
|
+
}
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (inBlockComment) {
|
|
623
|
+
if (current === "*" && next === "/") {
|
|
624
|
+
inBlockComment = false;
|
|
625
|
+
index += 1;
|
|
626
|
+
}
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
if (inString) {
|
|
630
|
+
result += current;
|
|
631
|
+
if (current === "\"" && !isEscaped(text, index)) inString = false;
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
if (current === "\"") {
|
|
635
|
+
inString = true;
|
|
636
|
+
result += current;
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (current === "/" && next === "/") {
|
|
640
|
+
inLineComment = true;
|
|
641
|
+
index += 1;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (current === "/" && next === "*") {
|
|
645
|
+
inBlockComment = true;
|
|
646
|
+
index += 1;
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
result += current;
|
|
650
|
+
}
|
|
651
|
+
if (inBlockComment) throw new Error("Unterminated block comment in JSONC config.");
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/cli/config/toml-tokens.ts
|
|
656
|
+
function stripInlineComment$1(line) {
|
|
657
|
+
let inSingle = false;
|
|
658
|
+
let inDouble = false;
|
|
659
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
660
|
+
const current = line[index] ?? "";
|
|
661
|
+
const previous = line[index - 1] ?? "";
|
|
662
|
+
if (current === "'" && !inDouble) {
|
|
663
|
+
inSingle = !inSingle;
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (current === "\"" && !inSingle && previous !== "\\") {
|
|
667
|
+
inDouble = !inDouble;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (current === "#" && !inSingle && !inDouble) return line.slice(0, index);
|
|
671
|
+
}
|
|
672
|
+
return line;
|
|
673
|
+
}
|
|
674
|
+
function splitPath(path, rawLine) {
|
|
675
|
+
const parts = path.split(".").map((part) => part.trim());
|
|
676
|
+
if (parts.some((part) => part.length === 0)) throw new Error(`Invalid TOML key: ${rawLine}`);
|
|
677
|
+
return parts;
|
|
678
|
+
}
|
|
679
|
+
function tokenizeTomlLine(rawLine, currentTable) {
|
|
680
|
+
const line = stripInlineComment$1(rawLine).trim();
|
|
681
|
+
if (!line) return;
|
|
682
|
+
const tableMatch = line.match(/^\[([A-Za-z0-9_.-]+)]$/);
|
|
683
|
+
if (tableMatch) return {
|
|
684
|
+
kind: "table",
|
|
685
|
+
path: splitPath(tableMatch[1] ?? "", rawLine),
|
|
686
|
+
rawLine
|
|
687
|
+
};
|
|
688
|
+
const separatorIndex = line.indexOf("=");
|
|
689
|
+
if (separatorIndex <= 0) throw new Error(`Invalid TOML assignment: ${rawLine}`);
|
|
690
|
+
const rawKey = line.slice(0, separatorIndex).trim();
|
|
691
|
+
const rawValue = line.slice(separatorIndex + 1).trim();
|
|
692
|
+
if (!rawKey || !rawValue) throw new Error(`Invalid TOML assignment: ${rawLine}`);
|
|
693
|
+
return {
|
|
694
|
+
kind: "assignment",
|
|
695
|
+
keyPath: [...currentTable, ...splitPath(rawKey, rawLine)],
|
|
696
|
+
rawValue,
|
|
697
|
+
rawLine
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function tokenizeTomlConfig(text) {
|
|
701
|
+
const tokens = [];
|
|
702
|
+
let currentTable = [];
|
|
703
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
704
|
+
const token = tokenizeTomlLine(rawLine, currentTable);
|
|
705
|
+
if (!token) continue;
|
|
706
|
+
if (token.kind === "table") {
|
|
707
|
+
currentTable = token.path;
|
|
708
|
+
tokens.push(token);
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
tokens.push(token);
|
|
712
|
+
}
|
|
713
|
+
return tokens;
|
|
714
|
+
}
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region src/cli/config/toml.ts
|
|
717
|
+
function isRecord(value) {
|
|
718
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
719
|
+
}
|
|
720
|
+
function parseTomlString(rawValue) {
|
|
721
|
+
if (rawValue.length < 2) throw new Error(`Invalid TOML string value: ${rawValue}`);
|
|
722
|
+
const quote = rawValue[0];
|
|
723
|
+
const inner = rawValue.slice(1, -1);
|
|
724
|
+
if (quote === "'") return inner;
|
|
725
|
+
return inner.replace(/\\(["\\bfnrt])/g, (_match, escaped) => {
|
|
726
|
+
switch (escaped) {
|
|
727
|
+
case "\"": return "\"";
|
|
728
|
+
case "\\": return "\\";
|
|
729
|
+
case "b": return "\b";
|
|
730
|
+
case "f": return "\f";
|
|
731
|
+
case "n": return "\n";
|
|
732
|
+
case "r": return "\r";
|
|
733
|
+
case "t": return " ";
|
|
734
|
+
default: return escaped;
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function splitTomlArrayItems(rawValue) {
|
|
739
|
+
const items = [];
|
|
740
|
+
let buffer = "";
|
|
741
|
+
let inSingle = false;
|
|
742
|
+
let inDouble = false;
|
|
743
|
+
for (let index = 0; index < rawValue.length; index += 1) {
|
|
744
|
+
const current = rawValue[index] ?? "";
|
|
745
|
+
const previous = rawValue[index - 1] ?? "";
|
|
746
|
+
if (current === "'" && !inDouble) {
|
|
747
|
+
inSingle = !inSingle;
|
|
748
|
+
buffer += current;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (current === "\"" && !inSingle && previous !== "\\") {
|
|
752
|
+
inDouble = !inDouble;
|
|
753
|
+
buffer += current;
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (current === "," && !inSingle && !inDouble) {
|
|
757
|
+
items.push(buffer.trim());
|
|
758
|
+
buffer = "";
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
buffer += current;
|
|
762
|
+
}
|
|
763
|
+
if (buffer.trim().length > 0) items.push(buffer.trim());
|
|
764
|
+
return items;
|
|
765
|
+
}
|
|
766
|
+
function parseTomlValue(rawValue) {
|
|
767
|
+
const trimmed = rawValue.trim();
|
|
768
|
+
if (trimmed === "true") return true;
|
|
769
|
+
if (trimmed === "false") return false;
|
|
770
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return parseTomlString(trimmed);
|
|
771
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
772
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
773
|
+
if (inner.length === 0) return [];
|
|
774
|
+
return splitTomlArrayItems(inner).map((item) => {
|
|
775
|
+
const parsed = parseTomlValue(item);
|
|
776
|
+
if (Array.isArray(parsed)) throw new Error(`Nested TOML arrays are not supported: ${rawValue}`);
|
|
777
|
+
return parsed;
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
throw new Error(`Unsupported TOML value: ${rawValue}`);
|
|
781
|
+
}
|
|
782
|
+
function ensureObject(parent, key) {
|
|
783
|
+
const existing = parent[key];
|
|
784
|
+
if (existing === void 0) {
|
|
785
|
+
const next = {};
|
|
786
|
+
parent[key] = next;
|
|
787
|
+
return next;
|
|
788
|
+
}
|
|
789
|
+
if (!isRecord(existing)) throw new Error(`Cannot redefine non-table key as table: ${key}`);
|
|
790
|
+
return existing;
|
|
791
|
+
}
|
|
792
|
+
function setNestedValue(root, keyPath, value) {
|
|
793
|
+
let target = root;
|
|
794
|
+
for (let index = 0; index < keyPath.length - 1; index += 1) target = ensureObject(target, keyPath[index] ?? "");
|
|
795
|
+
const leafKey = keyPath[keyPath.length - 1] ?? "";
|
|
796
|
+
if (leafKey in target) throw new Error(`Duplicate TOML key: ${keyPath.join(".")}`);
|
|
797
|
+
target[leafKey] = value;
|
|
798
|
+
}
|
|
799
|
+
function parseTomlConfig(text) {
|
|
800
|
+
const result = {};
|
|
801
|
+
for (const token of tokenizeTomlConfig(text)) {
|
|
802
|
+
if (token.kind === "table") {
|
|
803
|
+
let tableTarget = result;
|
|
804
|
+
for (const part of token.path) tableTarget = ensureObject(tableTarget, part);
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
setNestedValue(result, token.keyPath, parseTomlValue(token.rawValue));
|
|
808
|
+
}
|
|
809
|
+
return result;
|
|
810
|
+
}
|
|
811
|
+
//#endregion
|
|
812
|
+
//#region src/cli/config/parse.ts
|
|
813
|
+
function parseJsonConfig(text, sourceLabel, format) {
|
|
814
|
+
try {
|
|
815
|
+
return JSON.parse(text);
|
|
816
|
+
} catch (error) {
|
|
817
|
+
throw new ConfigParseError(sourceLabel, format, error instanceof Error ? error.message : String(error));
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function parseConfigText(text, format, sourceLabel) {
|
|
821
|
+
let parsed;
|
|
822
|
+
if (format === "json") parsed = parseJsonConfig(text, sourceLabel, "json");
|
|
823
|
+
else if (format === "jsonc") try {
|
|
824
|
+
parsed = parseJsonConfig(stripJsonComments(text), sourceLabel, "jsonc");
|
|
825
|
+
} catch (error) {
|
|
826
|
+
if (error instanceof ConfigParseError) throw error;
|
|
827
|
+
throw new ConfigParseError(sourceLabel, "jsonc", error instanceof Error ? error.message : String(error));
|
|
828
|
+
}
|
|
829
|
+
else try {
|
|
830
|
+
parsed = parseTomlConfig(text);
|
|
831
|
+
} catch (error) {
|
|
832
|
+
throw new ConfigParseError(sourceLabel, "toml", error instanceof Error ? error.message : String(error));
|
|
833
|
+
}
|
|
834
|
+
try {
|
|
835
|
+
return normalizeWordCounterConfig(parsed, sourceLabel);
|
|
836
|
+
} catch (error) {
|
|
837
|
+
if (error instanceof ConfigValidationError) throw error;
|
|
838
|
+
throw new ConfigParseError(sourceLabel, format, error instanceof Error ? error.message : String(error));
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async function loadConfigFile(path, format) {
|
|
842
|
+
let text;
|
|
843
|
+
try {
|
|
844
|
+
text = await readFile(path, "utf8");
|
|
845
|
+
} catch (error) {
|
|
846
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
847
|
+
throw new Error(`Failed to read config file (${path}): ${message}`);
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
format,
|
|
851
|
+
path,
|
|
852
|
+
config: parseConfigText(text, format, path)
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
//#endregion
|
|
856
|
+
//#region src/cli/config/resolve.ts
|
|
857
|
+
async function resolveWordCounterConfig(options = {}) {
|
|
858
|
+
const discovered = await discoverConfigFiles(options);
|
|
859
|
+
const filesToLoad = [discovered.user, discovered.cwd].filter((item) => item !== void 0);
|
|
860
|
+
const loadedFiles = await Promise.all(filesToLoad.map(async (item) => {
|
|
861
|
+
return {
|
|
862
|
+
...await loadConfigFile(item.path, item.format),
|
|
863
|
+
notes: item.notes
|
|
864
|
+
};
|
|
865
|
+
}));
|
|
866
|
+
let config = {};
|
|
867
|
+
const files = [];
|
|
868
|
+
const notes = [];
|
|
869
|
+
for (const loaded of loadedFiles) {
|
|
870
|
+
config = mergeWordCounterConfig(config, loaded.config);
|
|
871
|
+
files.push({
|
|
872
|
+
path: loaded.path,
|
|
873
|
+
format: loaded.format,
|
|
874
|
+
config: loaded.config
|
|
875
|
+
});
|
|
876
|
+
notes.push(...loaded.notes);
|
|
877
|
+
}
|
|
878
|
+
config = mergeWordCounterConfig(config, resolveEnvConfig(options.env));
|
|
879
|
+
return {
|
|
880
|
+
config,
|
|
881
|
+
files,
|
|
882
|
+
notes
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
//#endregion
|
|
886
|
+
//#region src/cli/config/sources.ts
|
|
887
|
+
function isCliOptionSource(program, optionName) {
|
|
888
|
+
return program.getOptionValueSource(optionName) === "cli";
|
|
889
|
+
}
|
|
890
|
+
function deriveCountCliSources(program) {
|
|
891
|
+
return {
|
|
892
|
+
detector: isCliOptionSource(program, "detector"),
|
|
893
|
+
contentGate: isCliOptionSource(program, "contentGate"),
|
|
894
|
+
pathMode: isCliOptionSource(program, "pathMode"),
|
|
895
|
+
recursive: isCliOptionSource(program, "recursive"),
|
|
896
|
+
includeExt: isCliOptionSource(program, "includeExt"),
|
|
897
|
+
excludeExt: isCliOptionSource(program, "excludeExt"),
|
|
898
|
+
totalOf: isCliOptionSource(program, "totalOf"),
|
|
899
|
+
debug: isCliOptionSource(program, "debug"),
|
|
900
|
+
verbose: isCliOptionSource(program, "verbose"),
|
|
901
|
+
debugReport: isCliOptionSource(program, "debugReport"),
|
|
902
|
+
debugReportTee: isCliOptionSource(program, "debugReportTee") || isCliOptionSource(program, "debugTee"),
|
|
903
|
+
progress: isCliOptionSource(program, "progress"),
|
|
904
|
+
quietSkips: isCliOptionSource(program, "quietSkips")
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
function deriveInitialCountProgressMode(program, rawProgressValue) {
|
|
908
|
+
if (!isCliOptionSource(program, "progress")) return "auto";
|
|
909
|
+
return rawProgressValue ? "on" : "off";
|
|
910
|
+
}
|
|
911
|
+
//#endregion
|
|
34
912
|
//#region src/cli/debug/channel.ts
|
|
35
913
|
const DEBUG_EVENT_SCHEMA_VERSION = 1;
|
|
36
914
|
const NOOP_CLOSE = async () => {};
|
|
@@ -333,6 +1211,7 @@ async function countBatchInputsWithWorkerJobs(filePaths, options) {
|
|
|
333
1211
|
section: options.section,
|
|
334
1212
|
detectorMode: options.detectorMode ?? "regex",
|
|
335
1213
|
wcOptions: options.wcOptions,
|
|
1214
|
+
detectBinary: options.detectBinary ?? true,
|
|
336
1215
|
preserveCollectorSegments: options.preserveCollectorSegments,
|
|
337
1216
|
detectorEvidence: options.detectorEvidence,
|
|
338
1217
|
debugVerbosity: options.debugVerbosity,
|
|
@@ -354,8 +1233,12 @@ async function countBatchInputsWithWorkerJobs(filePaths, options) {
|
|
|
354
1233
|
}
|
|
355
1234
|
//#endregion
|
|
356
1235
|
//#region src/cli/doctor/checks.ts
|
|
357
|
-
const REQUIRED_NODE_RANGE = ">=
|
|
358
|
-
const
|
|
1236
|
+
const REQUIRED_NODE_RANGE = ">=22.18.0";
|
|
1237
|
+
const REQUIRED_NODE_VERSION = {
|
|
1238
|
+
major: 22,
|
|
1239
|
+
minor: 18,
|
|
1240
|
+
patch: 0
|
|
1241
|
+
};
|
|
359
1242
|
const SAMPLE_TEXT = "Hello 世界";
|
|
360
1243
|
function normalizePackageVersion(value) {
|
|
361
1244
|
const trimmed = value?.trim();
|
|
@@ -368,22 +1251,35 @@ function deriveBuildChannel(packageVersion) {
|
|
|
368
1251
|
if (channel === "alpha" || channel === "beta" || channel === "rc" || channel === "canary") return channel;
|
|
369
1252
|
return "stable";
|
|
370
1253
|
}
|
|
371
|
-
function
|
|
372
|
-
const match = /^v?(\d+)(
|
|
1254
|
+
function parseNodeVersion(version) {
|
|
1255
|
+
const match = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:[-+].*)?$/.exec(version.trim());
|
|
373
1256
|
if (!match) return null;
|
|
374
1257
|
const major = Number.parseInt(match[1] ?? "", 10);
|
|
375
|
-
|
|
1258
|
+
const minor = Number.parseInt(match[2] ?? "0", 10);
|
|
1259
|
+
const patch = Number.parseInt(match[3] ?? "0", 10);
|
|
1260
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor) || !Number.isFinite(patch)) return null;
|
|
1261
|
+
return {
|
|
1262
|
+
major,
|
|
1263
|
+
minor,
|
|
1264
|
+
patch
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function meetsRequiredNodeVersion(version) {
|
|
1268
|
+
if (!version) return false;
|
|
1269
|
+
if (version.major !== REQUIRED_NODE_VERSION.major) return version.major > REQUIRED_NODE_VERSION.major;
|
|
1270
|
+
if (version.minor !== REQUIRED_NODE_VERSION.minor) return version.minor > REQUIRED_NODE_VERSION.minor;
|
|
1271
|
+
return version.patch >= REQUIRED_NODE_VERSION.patch;
|
|
376
1272
|
}
|
|
377
1273
|
function resolveRuntimeSummary(overrides = {}) {
|
|
378
|
-
const packageVersion = normalizePackageVersion(overrides.packageVersion ?? "0.1.5
|
|
1274
|
+
const packageVersion = normalizePackageVersion(overrides.packageVersion ?? "0.1.5");
|
|
379
1275
|
const nodeVersion = overrides.nodeVersion ?? process.version;
|
|
380
|
-
const
|
|
1276
|
+
const parsedNodeVersion = parseNodeVersion(nodeVersion);
|
|
381
1277
|
return {
|
|
382
1278
|
packageVersion,
|
|
383
1279
|
buildChannel: deriveBuildChannel(packageVersion),
|
|
384
1280
|
requiredNodeRange: REQUIRED_NODE_RANGE,
|
|
385
1281
|
nodeVersion,
|
|
386
|
-
meetsProjectRequirement:
|
|
1282
|
+
meetsProjectRequirement: meetsRequiredNodeVersion(parsedNodeVersion),
|
|
387
1283
|
platform: overrides.platform ?? process.platform,
|
|
388
1284
|
arch: overrides.arch ?? process.arch
|
|
389
1285
|
};
|
|
@@ -3698,100 +4594,6 @@ async function resolveBatchFilePaths(pathInputs, options) {
|
|
|
3698
4594
|
};
|
|
3699
4595
|
}
|
|
3700
4596
|
//#endregion
|
|
3701
|
-
//#region src/cli/total-of.ts
|
|
3702
|
-
const TOTAL_OF_PARTS = Object.freeze([
|
|
3703
|
-
"words",
|
|
3704
|
-
"emoji",
|
|
3705
|
-
"symbols",
|
|
3706
|
-
"punctuation",
|
|
3707
|
-
"whitespace"
|
|
3708
|
-
]);
|
|
3709
|
-
const TOTAL_OF_PART_ALIASES = {
|
|
3710
|
-
word: "words",
|
|
3711
|
-
words: "words",
|
|
3712
|
-
emoji: "emoji",
|
|
3713
|
-
emojis: "emoji",
|
|
3714
|
-
symbol: "symbols",
|
|
3715
|
-
symbols: "symbols",
|
|
3716
|
-
punction: "punctuation",
|
|
3717
|
-
punctuation: "punctuation",
|
|
3718
|
-
whitespace: "whitespace"
|
|
3719
|
-
};
|
|
3720
|
-
function createTotalOfCounts() {
|
|
3721
|
-
return {
|
|
3722
|
-
words: 0,
|
|
3723
|
-
emoji: 0,
|
|
3724
|
-
symbols: 0,
|
|
3725
|
-
punctuation: 0,
|
|
3726
|
-
whitespace: 0
|
|
3727
|
-
};
|
|
3728
|
-
}
|
|
3729
|
-
function collectNonWordCounts(target, nonWords) {
|
|
3730
|
-
if (!nonWords) return;
|
|
3731
|
-
target.emoji += nonWords.counts.emoji;
|
|
3732
|
-
target.symbols += nonWords.counts.symbols;
|
|
3733
|
-
target.punctuation += nonWords.counts.punctuation;
|
|
3734
|
-
target.whitespace += nonWords.counts.whitespace ?? 0;
|
|
3735
|
-
}
|
|
3736
|
-
function collectFromWordCounterResult(result) {
|
|
3737
|
-
const counts = createTotalOfCounts();
|
|
3738
|
-
counts.words += result.counts?.words ?? result.total;
|
|
3739
|
-
if (result.breakdown.mode === "collector") {
|
|
3740
|
-
collectNonWordCounts(counts, result.breakdown.nonWords);
|
|
3741
|
-
return counts;
|
|
3742
|
-
}
|
|
3743
|
-
for (const item of result.breakdown.items) collectNonWordCounts(counts, item.nonWords);
|
|
3744
|
-
return counts;
|
|
3745
|
-
}
|
|
3746
|
-
function collectTotalOfCounts(result) {
|
|
3747
|
-
if (!("section" in result)) return collectFromWordCounterResult(result);
|
|
3748
|
-
const counts = createTotalOfCounts();
|
|
3749
|
-
for (const item of result.items) {
|
|
3750
|
-
const itemCounts = collectFromWordCounterResult(item.result);
|
|
3751
|
-
for (const part of TOTAL_OF_PARTS) counts[part] += itemCounts[part];
|
|
3752
|
-
}
|
|
3753
|
-
return counts;
|
|
3754
|
-
}
|
|
3755
|
-
function parseTotalOfToken(token) {
|
|
3756
|
-
const canonical = TOTAL_OF_PART_ALIASES[token.trim().toLowerCase()];
|
|
3757
|
-
if (canonical) return canonical;
|
|
3758
|
-
throw new Error(`Invalid --total-of part: ${token}. Allowed: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
3759
|
-
}
|
|
3760
|
-
function parseTotalOfOption(value) {
|
|
3761
|
-
const rawTokens = value.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
3762
|
-
if (rawTokens.length === 0) throw new Error(`Invalid --total-of value: "${value}". Use comma-separated parts from: ${TOTAL_OF_PARTS.join(", ")}.`);
|
|
3763
|
-
const parts = [];
|
|
3764
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3765
|
-
for (const token of rawTokens) {
|
|
3766
|
-
const parsed = parseTotalOfToken(token);
|
|
3767
|
-
if (seen.has(parsed)) continue;
|
|
3768
|
-
seen.add(parsed);
|
|
3769
|
-
parts.push(parsed);
|
|
3770
|
-
}
|
|
3771
|
-
return parts;
|
|
3772
|
-
}
|
|
3773
|
-
function requiresNonWordCollection(parts) {
|
|
3774
|
-
if (!parts || parts.length === 0) return false;
|
|
3775
|
-
return parts.some((part) => part !== "words");
|
|
3776
|
-
}
|
|
3777
|
-
function requiresWhitespaceCollection(parts) {
|
|
3778
|
-
if (!parts || parts.length === 0) return false;
|
|
3779
|
-
return parts.includes("whitespace");
|
|
3780
|
-
}
|
|
3781
|
-
function resolveTotalOfOverride(result, parts) {
|
|
3782
|
-
if (!parts || parts.length === 0) return;
|
|
3783
|
-
const counts = collectTotalOfCounts(result);
|
|
3784
|
-
let total = 0;
|
|
3785
|
-
for (const part of parts) total += counts[part];
|
|
3786
|
-
return {
|
|
3787
|
-
parts: [...parts],
|
|
3788
|
-
total
|
|
3789
|
-
};
|
|
3790
|
-
}
|
|
3791
|
-
function formatTotalOfParts(parts) {
|
|
3792
|
-
return parts.join(", ");
|
|
3793
|
-
}
|
|
3794
|
-
//#endregion
|
|
3795
4597
|
//#region src/cli/runtime/options.ts
|
|
3796
4598
|
function hasPathInput(pathValues) {
|
|
3797
4599
|
return Array.isArray(pathValues) && pathValues.length > 0;
|
|
@@ -3929,10 +4731,10 @@ function selectInspectText(input, section) {
|
|
|
3929
4731
|
if (section === "frontmatter") return parsed.frontmatter ?? "";
|
|
3930
4732
|
return parsed.content;
|
|
3931
4733
|
}
|
|
3932
|
-
async function loadSingleInspectInput(path, textTokens, section) {
|
|
4734
|
+
async function loadSingleInspectInput(path, textTokens, section, detectBinary = true) {
|
|
3933
4735
|
if (path) try {
|
|
3934
4736
|
const buffer = await readFile(path);
|
|
3935
|
-
if (isProbablyBinary(buffer)) throw new Error("binary file");
|
|
4737
|
+
if (detectBinary && isProbablyBinary(buffer)) throw new Error("binary file");
|
|
3936
4738
|
return {
|
|
3937
4739
|
text: selectInspectText(buffer.toString("utf8"), section),
|
|
3938
4740
|
sourceType: "path",
|
|
@@ -3986,7 +4788,7 @@ async function loadInspectBatchInputs(pathInputs, options) {
|
|
|
3986
4788
|
});
|
|
3987
4789
|
continue;
|
|
3988
4790
|
}
|
|
3989
|
-
if (isProbablyBinary(buffer)) {
|
|
4791
|
+
if (options.pathDetectBinary && isProbablyBinary(buffer)) {
|
|
3990
4792
|
if (entry.source === "direct") failures.push({
|
|
3991
4793
|
path: entry.path,
|
|
3992
4794
|
reason: "binary file"
|
|
@@ -4165,13 +4967,14 @@ const INSPECT_HELP_LINES = [
|
|
|
4165
4967
|
"inspect detector behavior without count output",
|
|
4166
4968
|
"",
|
|
4167
4969
|
"Options:",
|
|
4168
|
-
" --detector <mode> inspect detector mode (wasm, regex) (default:
|
|
4970
|
+
" -d, --detector <mode> inspect detector mode (wasm, regex) (default: regex)",
|
|
4169
4971
|
" --content-gate <mode> content gate mode (default, strict, loose, off) (default: default)",
|
|
4170
4972
|
" --view <view> inspect view (pipeline, engine) (default: pipeline)",
|
|
4171
4973
|
" -f, --format <format> inspect output format (standard, json) (default: standard)",
|
|
4172
4974
|
" --pretty pretty print inspect JSON output",
|
|
4173
4975
|
" --section <section> inspect section selector (all, frontmatter, content) (default: all)",
|
|
4174
4976
|
" --path-mode <mode> path resolution mode for --path inputs (auto, manual) (default: auto)",
|
|
4977
|
+
" --recursive enable recursive directory traversal for --path directories",
|
|
4175
4978
|
" --no-recursive disable recursive directory traversal for --path directories",
|
|
4176
4979
|
" --include-ext <exts> comma-separated extensions to include during directory scanning",
|
|
4177
4980
|
" --exclude-ext <exts> comma-separated extensions to exclude during directory scanning",
|
|
@@ -4185,7 +4988,7 @@ function printInspectHelp() {
|
|
|
4185
4988
|
//#endregion
|
|
4186
4989
|
//#region src/cli/inspect/parse.ts
|
|
4187
4990
|
function parseDetector(rawValue) {
|
|
4188
|
-
if (rawValue === void 0) return "
|
|
4991
|
+
if (rawValue === void 0) return "regex";
|
|
4189
4992
|
if (rawValue === "wasm" || rawValue === "regex") return rawValue;
|
|
4190
4993
|
return null;
|
|
4191
4994
|
}
|
|
@@ -4212,13 +5015,10 @@ function parsePathMode(rawValue) {
|
|
|
4212
5015
|
function isSupportedInspectSectionMode(value) {
|
|
4213
5016
|
return value === "all" || value === "frontmatter" || value === "content";
|
|
4214
5017
|
}
|
|
4215
|
-
function isInvalidInspectDetectorViewCombination(detector, view) {
|
|
4216
|
-
return view === "engine" && detector === "regex";
|
|
4217
|
-
}
|
|
4218
5018
|
function validateInspectInvocation(argv) {
|
|
4219
5019
|
const inspectIndex = argv.findIndex((token, index) => index >= 2 && token === "inspect");
|
|
4220
5020
|
const tokens = inspectIndex >= 0 ? argv.slice(inspectIndex + 1) : [];
|
|
4221
|
-
let detector = "
|
|
5021
|
+
let detector = "regex";
|
|
4222
5022
|
let contentGateMode = "default";
|
|
4223
5023
|
let view = "pipeline";
|
|
4224
5024
|
let format = "standard";
|
|
@@ -4231,6 +5031,14 @@ function validateInspectInvocation(argv) {
|
|
|
4231
5031
|
const excludeExt = [];
|
|
4232
5032
|
let regex;
|
|
4233
5033
|
const textTokens = [];
|
|
5034
|
+
const sources = {
|
|
5035
|
+
detector: false,
|
|
5036
|
+
contentGate: false,
|
|
5037
|
+
pathMode: false,
|
|
5038
|
+
recursive: false,
|
|
5039
|
+
includeExt: false,
|
|
5040
|
+
excludeExt: false
|
|
5041
|
+
};
|
|
4234
5042
|
let expects = null;
|
|
4235
5043
|
let positionalMode = false;
|
|
4236
5044
|
const consumeValue = (kind, value) => {
|
|
@@ -4238,6 +5046,7 @@ function validateInspectInvocation(argv) {
|
|
|
4238
5046
|
const parsed = parseDetector(value);
|
|
4239
5047
|
if (parsed === null) return "`--detector` must be `wasm` or `regex`.";
|
|
4240
5048
|
detector = parsed;
|
|
5049
|
+
sources.detector = true;
|
|
4241
5050
|
return null;
|
|
4242
5051
|
}
|
|
4243
5052
|
if (kind === "view") {
|
|
@@ -4250,6 +5059,7 @@ function validateInspectInvocation(argv) {
|
|
|
4250
5059
|
const parsed = parseContentGateMode(value);
|
|
4251
5060
|
if (parsed === null) return "`--content-gate` must be `default`, `strict`, `loose`, or `off`.";
|
|
4252
5061
|
contentGateMode = parsed;
|
|
5062
|
+
sources.contentGate = true;
|
|
4253
5063
|
return null;
|
|
4254
5064
|
}
|
|
4255
5065
|
if (kind === "format") {
|
|
@@ -4268,6 +5078,7 @@ function validateInspectInvocation(argv) {
|
|
|
4268
5078
|
const parsed = parsePathMode(value);
|
|
4269
5079
|
if (parsed === null) return "`--path-mode` must be `auto` or `manual`.";
|
|
4270
5080
|
pathMode = parsed;
|
|
5081
|
+
sources.pathMode = true;
|
|
4271
5082
|
return null;
|
|
4272
5083
|
}
|
|
4273
5084
|
if (kind === "path") {
|
|
@@ -4276,10 +5087,12 @@ function validateInspectInvocation(argv) {
|
|
|
4276
5087
|
}
|
|
4277
5088
|
if (kind === "includeExt") {
|
|
4278
5089
|
includeExt.push(value);
|
|
5090
|
+
sources.includeExt = true;
|
|
4279
5091
|
return null;
|
|
4280
5092
|
}
|
|
4281
5093
|
if (kind === "excludeExt") {
|
|
4282
5094
|
excludeExt.push(value);
|
|
5095
|
+
sources.excludeExt = true;
|
|
4283
5096
|
return null;
|
|
4284
5097
|
}
|
|
4285
5098
|
if (regex !== void 0) return "`--regex` can only be provided once.";
|
|
@@ -4308,16 +5121,22 @@ function validateInspectInvocation(argv) {
|
|
|
4308
5121
|
positionalMode = true;
|
|
4309
5122
|
continue;
|
|
4310
5123
|
}
|
|
5124
|
+
if (token === "--recursive") {
|
|
5125
|
+
recursive = true;
|
|
5126
|
+
sources.recursive = true;
|
|
5127
|
+
continue;
|
|
5128
|
+
}
|
|
4311
5129
|
if (token === "--no-recursive") {
|
|
4312
5130
|
recursive = false;
|
|
5131
|
+
sources.recursive = true;
|
|
4313
5132
|
continue;
|
|
4314
5133
|
}
|
|
4315
5134
|
if (token === "--pretty") {
|
|
4316
5135
|
pretty = true;
|
|
4317
5136
|
continue;
|
|
4318
5137
|
}
|
|
4319
|
-
if (token === "--detector" || token === "--content-gate" || token === "--view" || token === "--format" || token === "-f" || token === "--section" || token === "--path-mode" || token === "--path" || token === "-p" || token === "--include-ext" || token === "--exclude-ext" || token === "--regex") {
|
|
4320
|
-
expects = token === "-p" ? "path" : token === "--content-gate" ? "contentGate" : token === "-f" ? "format" : token === "--path-mode" ? "pathMode" : token === "--include-ext" ? "includeExt" : token === "--exclude-ext" ? "excludeExt" : token.slice(2);
|
|
5138
|
+
if (token === "--detector" || token === "-d" || token === "--content-gate" || token === "--view" || token === "--format" || token === "-f" || token === "--section" || token === "--path-mode" || token === "--path" || token === "-p" || token === "--include-ext" || token === "--exclude-ext" || token === "--regex") {
|
|
5139
|
+
expects = token === "-p" ? "path" : token === "-d" ? "detector" : token === "--content-gate" ? "contentGate" : token === "-f" ? "format" : token === "--path-mode" ? "pathMode" : token === "--include-ext" ? "includeExt" : token === "--exclude-ext" ? "excludeExt" : token.slice(2);
|
|
4321
5140
|
continue;
|
|
4322
5141
|
}
|
|
4323
5142
|
if (token.startsWith("--detector=") || token.startsWith("--content-gate=") || token.startsWith("--view=") || token.startsWith("--format=") || token.startsWith("--section=") || token.startsWith("--path-mode=") || token.startsWith("--path=") || token.startsWith("--include-ext=") || token.startsWith("--exclude-ext=") || token.startsWith("--regex=")) {
|
|
@@ -4354,10 +5173,6 @@ function validateInspectInvocation(argv) {
|
|
|
4354
5173
|
ok: false,
|
|
4355
5174
|
message: "No inspect input provided. Pass text or use --path."
|
|
4356
5175
|
};
|
|
4357
|
-
if (isInvalidInspectDetectorViewCombination(detector, view)) return {
|
|
4358
|
-
ok: false,
|
|
4359
|
-
message: "`--view engine` requires `--detector wasm`."
|
|
4360
|
-
};
|
|
4361
5176
|
return {
|
|
4362
5177
|
ok: true,
|
|
4363
5178
|
detector,
|
|
@@ -4367,12 +5182,14 @@ function validateInspectInvocation(argv) {
|
|
|
4367
5182
|
pretty,
|
|
4368
5183
|
section,
|
|
4369
5184
|
pathMode,
|
|
5185
|
+
pathDetectBinary: true,
|
|
4370
5186
|
recursive,
|
|
4371
5187
|
includeExt,
|
|
4372
5188
|
excludeExt,
|
|
4373
5189
|
...regex !== void 0 ? { regex } : {},
|
|
4374
5190
|
paths,
|
|
4375
|
-
textTokens
|
|
5191
|
+
textTokens,
|
|
5192
|
+
sources
|
|
4376
5193
|
};
|
|
4377
5194
|
}
|
|
4378
5195
|
//#endregion
|
|
@@ -4400,25 +5217,52 @@ async function runSingleInspect(validated, input) {
|
|
|
4400
5217
|
function isExplicitInspectInvocation(argv) {
|
|
4401
5218
|
return argv[2] === "inspect";
|
|
4402
5219
|
}
|
|
4403
|
-
|
|
4404
|
-
const
|
|
4405
|
-
|
|
4406
|
-
console.error(import_picocolors.default.
|
|
5220
|
+
function emitConfigNotes$1(notes) {
|
|
5221
|
+
for (const note of notes) {
|
|
5222
|
+
const warningLine = note.startsWith("Warning:") ? note : `Warning: ${note}`;
|
|
5223
|
+
console.error(import_picocolors.default.yellow(warningLine));
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
async function executeInspectCommand({ argv, runtime }) {
|
|
5227
|
+
const parsed = validateInspectInvocation(argv);
|
|
5228
|
+
if (!parsed.ok) {
|
|
5229
|
+
console.error(import_picocolors.default.red(`error: ${parsed.message}`));
|
|
4407
5230
|
process.exitCode = 1;
|
|
4408
5231
|
return;
|
|
4409
5232
|
}
|
|
4410
|
-
if (
|
|
5233
|
+
if (parsed.help) {
|
|
4411
5234
|
printInspectHelp();
|
|
4412
5235
|
process.exitCode = 0;
|
|
4413
5236
|
return;
|
|
4414
5237
|
}
|
|
5238
|
+
let validated = parsed;
|
|
5239
|
+
try {
|
|
5240
|
+
const resolvedConfig = await resolveWordCounterConfig({
|
|
5241
|
+
env: runtime?.env,
|
|
5242
|
+
cwd: runtime?.cwd
|
|
5243
|
+
});
|
|
5244
|
+
validated = applyConfigToInspectInvocation(validated, resolvedConfig.config);
|
|
5245
|
+
emitConfigNotes$1(resolvedConfig.notes);
|
|
5246
|
+
} catch (error) {
|
|
5247
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5248
|
+
console.error(import_picocolors.default.red(`error: ${message}`));
|
|
5249
|
+
process.exitCode = 1;
|
|
5250
|
+
return;
|
|
5251
|
+
}
|
|
5252
|
+
if (validated.view === "engine" && validated.detector === "regex") {
|
|
5253
|
+
console.error(import_picocolors.default.red("error: `--view engine` requires `--detector wasm`."));
|
|
5254
|
+
process.exitCode = 1;
|
|
5255
|
+
return;
|
|
5256
|
+
}
|
|
4415
5257
|
try {
|
|
4416
5258
|
if (validated.paths.length === 0) {
|
|
4417
|
-
|
|
5259
|
+
const input = await loadSingleInspectInput(void 0, validated.textTokens, validated.section);
|
|
5260
|
+
await runSingleInspect(validated, input);
|
|
4418
5261
|
return;
|
|
4419
5262
|
}
|
|
4420
5263
|
const loaded = await loadInspectBatchInputs(validated.paths, {
|
|
4421
5264
|
pathMode: validated.pathMode,
|
|
5265
|
+
pathDetectBinary: validated.pathDetectBinary,
|
|
4422
5266
|
recursive: validated.recursive,
|
|
4423
5267
|
includeExt: validated.includeExt,
|
|
4424
5268
|
excludeExt: validated.excludeExt,
|
|
@@ -4484,7 +5328,7 @@ function parseJobsOption(value) {
|
|
|
4484
5328
|
return parsed;
|
|
4485
5329
|
}
|
|
4486
5330
|
function configureProgramOptions(program, parseMode) {
|
|
4487
|
-
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("--detector <mode>", "locale detector mode").choices(DETECTOR_CHOICES).default("regex")).addOption(new Option("--content-gate <mode>", "detector content gate policy mode (default, strict, loose, off)").choices(CONTENT_GATE_CHOICES)).addOption(new Option("--path-mode <mode>", "path resolution mode: auto (default) expands directories; manual treats --path values as literal files").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("--latin-hint <tag>=<pattern>", "add a custom Latin hint rule (repeatable)", collectLatinHintValue, []).option("--latin-hints-file <path>", "load custom Latin hint rules from a JSON file").option("--no-default-latin-hints", "disable built-in Latin hint rules").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 --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("--detector-evidence", "emit per-window detector evidence on the debug stream (requires --debug and --detector wasm)").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("--jobs <n>", "batch jobs in --path mode (1=async main-thread, >1=worker load+count)", parseJobsOption, 1).option("--print-jobs-limit", "print host jobs-limit JSON and exit (must be used alone)").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-warnings", "suppress non-fatal warning diagnostics").option("--quiet-skips", "suppress debug skip output and per-file json skipped field").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("--regex <pattern>", "regex filter for directory-scanned paths (applies to --path directories only)").option("-p, --path <path>", "read input from file or directory (directories expand in auto mode by default)", collectPathValue, []).argument("[text...]", "text to count").showHelpAfterError();
|
|
5331
|
+
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("-d, --detector <mode>", "locale detector mode").choices(DETECTOR_CHOICES).default("regex")).addOption(new Option("--content-gate <mode>", "detector content gate policy mode (default, strict, loose, off)").choices(CONTENT_GATE_CHOICES)).addOption(new Option("--path-mode <mode>", "path resolution mode: auto (default) expands directories; manual treats --path values as literal files").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("--latin-hint <tag>=<pattern>", "add a custom Latin hint rule (repeatable)", collectLatinHintValue, []).option("--latin-hints-file <path>", "load custom Latin hint rules from a JSON file").option("--no-default-latin-hints", "disable built-in Latin hint rules").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 --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("--detector-evidence", "emit per-window detector evidence on the debug stream (requires --debug and --detector wasm)").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("--jobs <n>", "batch jobs in --path mode (1=async main-thread, >1=worker load+count)", parseJobsOption, 1).option("--print-jobs-limit", "print host jobs-limit JSON and exit (must be used alone)").option("--progress", "enable batch progress indicator").option("--no-progress", "disable batch progress indicator").option("--keep-progress", "keep final batch progress line visible in standard mode").option("--recursive", "enable recursive directory traversal").option("--no-recursive", "disable recursive directory traversal").option("--quiet-warnings", "suppress non-fatal warning diagnostics").option("--quiet-skips", "suppress debug skip output and per-file json skipped field").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("--regex <pattern>", "regex filter for directory-scanned paths (applies to --path directories only)").option("-p, --path <path>", "read input from file or directory (directories expand in auto mode by default)", collectPathValue, []).argument("[text...]", "text to count").showHelpAfterError();
|
|
4488
5332
|
}
|
|
4489
5333
|
//#endregion
|
|
4490
5334
|
//#region src/cli/program/version.ts
|
|
@@ -4518,7 +5362,7 @@ function normalizeVersion(value) {
|
|
|
4518
5362
|
return trimmed;
|
|
4519
5363
|
}
|
|
4520
5364
|
function resolvePackageVersion(options = {}) {
|
|
4521
|
-
const embeddedVersion = normalizeVersion(options.embeddedVersion ?? "0.1.5
|
|
5365
|
+
const embeddedVersion = normalizeVersion(options.embeddedVersion ?? "0.1.5");
|
|
4522
5366
|
if (embeddedVersion) return embeddedVersion;
|
|
4523
5367
|
const maxLevels = options.maxLevels ?? 8;
|
|
4524
5368
|
const resolveFromPath = options.resolveFromPath ?? resolveVersionFromPath;
|
|
@@ -4759,8 +5603,7 @@ function finalizeBatchSummaryFromFileResults(files, section, wcOptions, options
|
|
|
4759
5603
|
//#region src/cli/batch/jobs/queue.ts
|
|
4760
5604
|
async function runBoundedQueue(total, requestedJobs, worker) {
|
|
4761
5605
|
if (total === 0) return [];
|
|
4762
|
-
const
|
|
4763
|
-
const concurrency = Math.max(1, Math.min(total, safeRequestedJobs));
|
|
5606
|
+
const concurrency = Math.max(1, Math.min(total, Number.isFinite(requestedJobs) ? Math.floor(requestedJobs) : 1));
|
|
4764
5607
|
const results = Array.from({ length: total });
|
|
4765
5608
|
let nextIndex = 0;
|
|
4766
5609
|
const runWorker = async () => {
|
|
@@ -4794,7 +5637,7 @@ async function readBatchInput(path, options) {
|
|
|
4794
5637
|
reason: `not readable: ${error instanceof Error ? error.message : String(error)}`
|
|
4795
5638
|
};
|
|
4796
5639
|
}
|
|
4797
|
-
if (isProbablyBinary(buffer)) return {
|
|
5640
|
+
if (options.detectBinary && isProbablyBinary(buffer)) return {
|
|
4798
5641
|
type: "skip",
|
|
4799
5642
|
path,
|
|
4800
5643
|
reason: "binary file"
|
|
@@ -4815,7 +5658,8 @@ async function countBatchInputsWithJobs(filePaths, options) {
|
|
|
4815
5658
|
const entries = await runBoundedQueue(filePaths.length, options.jobs, async (index) => {
|
|
4816
5659
|
const loaded = await readBatchInput(filePaths[index], {
|
|
4817
5660
|
requestedJobs: options.jobs,
|
|
4818
|
-
limits
|
|
5661
|
+
limits,
|
|
5662
|
+
detectBinary: options.detectBinary ?? true
|
|
4819
5663
|
});
|
|
4820
5664
|
if (loaded.type === "skip") {
|
|
4821
5665
|
completed += 1;
|
|
@@ -5068,6 +5912,7 @@ async function runBatchCount(options) {
|
|
|
5068
5912
|
section: options.section,
|
|
5069
5913
|
detectorMode: options.wcOptions.detector ?? "regex",
|
|
5070
5914
|
wcOptions: options.wcOptions,
|
|
5915
|
+
detectBinary: options.detectBinary,
|
|
5071
5916
|
preserveCollectorSegments: options.preserveCollectorSegments,
|
|
5072
5917
|
detectorEvidence: options.detectorEvidence,
|
|
5073
5918
|
debugVerbosity: options.debug.verbosity,
|
|
@@ -5095,6 +5940,7 @@ async function runBatchCount(options) {
|
|
|
5095
5940
|
section: options.section,
|
|
5096
5941
|
detectorMode: options.wcOptions.detector ?? "regex",
|
|
5097
5942
|
wcOptions: options.wcOptions,
|
|
5943
|
+
detectBinary: options.detectBinary,
|
|
5098
5944
|
preserveCollectorSegments: options.preserveCollectorSegments,
|
|
5099
5945
|
detectorEvidence: options.detectorEvidence,
|
|
5100
5946
|
debugVerbosity: options.debug.verbosity,
|
|
@@ -5110,6 +5956,7 @@ async function runBatchCount(options) {
|
|
|
5110
5956
|
section: options.section,
|
|
5111
5957
|
detectorMode: options.wcOptions.detector ?? "regex",
|
|
5112
5958
|
wcOptions: options.wcOptions,
|
|
5959
|
+
detectBinary: options.detectBinary,
|
|
5113
5960
|
preserveCollectorSegments: options.preserveCollectorSegments,
|
|
5114
5961
|
detectorEvidence: options.detectorEvidence,
|
|
5115
5962
|
debugVerbosity: options.debug.verbosity,
|
|
@@ -5394,13 +6241,14 @@ async function executeBatchCount({ argv, options, runtime, resolved, debug, teeE
|
|
|
5394
6241
|
pathInputs: options.path ?? [],
|
|
5395
6242
|
batchOptions,
|
|
5396
6243
|
extensionFilter,
|
|
6244
|
+
detectBinary: options.pathDetectBinary,
|
|
5397
6245
|
section: options.section,
|
|
5398
6246
|
wcOptions: resolved.wcOptions,
|
|
5399
6247
|
preserveCollectorSegments: options.format === "json",
|
|
5400
6248
|
detectorEvidence: Boolean(options.detectorEvidence),
|
|
5401
6249
|
debug,
|
|
5402
6250
|
progressReporter: createBatchProgressReporter({
|
|
5403
|
-
enabled: options.format === "standard" && options.
|
|
6251
|
+
enabled: options.format === "standard" && options.progressMode !== "off",
|
|
5404
6252
|
stream: runtime.stderr ?? process.stderr,
|
|
5405
6253
|
clearOnFinish: !(mirrorDebugToTerminal || options.keepProgress)
|
|
5406
6254
|
}),
|
|
@@ -5603,6 +6451,12 @@ async function executeSingleCount({ textTokens, options, resolved, debug }) {
|
|
|
5603
6451
|
}
|
|
5604
6452
|
//#endregion
|
|
5605
6453
|
//#region src/command.ts
|
|
6454
|
+
function emitConfigNotes(notes) {
|
|
6455
|
+
for (const note of notes) {
|
|
6456
|
+
const warningLine = note.startsWith("Warning:") ? note : `Warning: ${note}`;
|
|
6457
|
+
console.error(import_picocolors.default.yellow(warningLine));
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
5606
6460
|
async function runCli(argv = process.argv, runtime = {}) {
|
|
5607
6461
|
if (isExplicitDoctorInvocation(argv)) {
|
|
5608
6462
|
await executeDoctorCommand({
|
|
@@ -5612,7 +6466,10 @@ async function runCli(argv = process.argv, runtime = {}) {
|
|
|
5612
6466
|
return;
|
|
5613
6467
|
}
|
|
5614
6468
|
if (isExplicitInspectInvocation(argv)) {
|
|
5615
|
-
await executeInspectCommand({
|
|
6469
|
+
await executeInspectCommand({
|
|
6470
|
+
argv,
|
|
6471
|
+
runtime
|
|
6472
|
+
});
|
|
5616
6473
|
return;
|
|
5617
6474
|
}
|
|
5618
6475
|
const program = new Command();
|
|
@@ -5623,8 +6480,8 @@ async function runCli(argv = process.argv, runtime = {}) {
|
|
|
5623
6480
|
};
|
|
5624
6481
|
program.name("word-counter").description("Locale-aware word counting powered by Intl.Segmenter.").version(getFormattedVersionLabel(), "-v, --version", "output the version number").addHelpText("after", "\nCommands:\n inspect [options] [text...] inspect detector behavior without count output\n doctor [options] report runtime diagnostics for this host");
|
|
5625
6482
|
configureProgramOptions(program, parseMode);
|
|
5626
|
-
program.action(async (textTokens,
|
|
5627
|
-
if (
|
|
6483
|
+
program.action(async (textTokens, rawOptions) => {
|
|
6484
|
+
if (rawOptions.printJobsLimit) {
|
|
5628
6485
|
try {
|
|
5629
6486
|
validateStandalonePrintJobsLimitUsage(argv);
|
|
5630
6487
|
} catch (error) {
|
|
@@ -5635,6 +6492,23 @@ async function runCli(argv = process.argv, runtime = {}) {
|
|
|
5635
6492
|
console.log(JSON.stringify(resolveBatchJobsLimit()));
|
|
5636
6493
|
return;
|
|
5637
6494
|
}
|
|
6495
|
+
let options = {
|
|
6496
|
+
...rawOptions,
|
|
6497
|
+
pathDetectBinary: rawOptions.pathDetectBinary ?? true,
|
|
6498
|
+
progressMode: deriveInitialCountProgressMode(program, rawOptions.progress)
|
|
6499
|
+
};
|
|
6500
|
+
try {
|
|
6501
|
+
const resolvedConfig = await resolveWordCounterConfig({
|
|
6502
|
+
env: runtime.env,
|
|
6503
|
+
cwd: runtime.cwd
|
|
6504
|
+
});
|
|
6505
|
+
options = applyConfigToCountOptions(options, resolvedConfig.config, deriveCountCliSources(program));
|
|
6506
|
+
if (!options.quietWarnings) emitConfigNotes(resolvedConfig.notes);
|
|
6507
|
+
} catch (error) {
|
|
6508
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6509
|
+
program.error(import_picocolors.default.red(message));
|
|
6510
|
+
return;
|
|
6511
|
+
}
|
|
5638
6512
|
const debugEnabled = Boolean(options.debug);
|
|
5639
6513
|
const debugReportPath = resolveDebugReportPathOption(options.debugReport);
|
|
5640
6514
|
const debugReportEnabled = options.debugReport !== void 0 && options.debugReport !== false;
|