@dev-pi2pie/word-counter 0.1.4 → 0.1.5-canary.1
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 +27 -0
- package/dist/cjs/index.cjs +7 -30
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/bin.mjs +487 -226
- package/dist/esm/bin.mjs.map +1 -1
- package/dist/esm/index.mjs +7 -29
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/worker/count-worker.mjs +8 -32
- package/dist/esm/worker/count-worker.mjs.map +1 -1
- package/dist/esm/worker-pool.mjs +5 -3
- package/dist/esm/worker-pool.mjs.map +1 -1
- package/package.json +5 -5
package/dist/esm/bin.mjs
CHANGED
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import { closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, statSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
6
|
import os from "node:os";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { parseDocument } from "yaml";
|
|
9
9
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
10
|
-
|
|
11
10
|
//#region \0rolldown/runtime.js
|
|
12
11
|
var __create = Object.create;
|
|
13
12
|
var __defProp = Object.defineProperty;
|
|
@@ -17,16 +16,12 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
17
16
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
18
17
|
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
18
|
var __copyProps = (to, from, except, desc) => {
|
|
20
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
}
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
20
|
+
key = keys[i];
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
22
|
+
get: ((k) => from[k]).bind(null, key),
|
|
23
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
24
|
+
});
|
|
30
25
|
}
|
|
31
26
|
return to;
|
|
32
27
|
};
|
|
@@ -34,7 +29,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
34
29
|
value: mod,
|
|
35
30
|
enumerable: true
|
|
36
31
|
}) : target, mod));
|
|
37
|
-
|
|
38
32
|
//#endregion
|
|
39
33
|
//#region src/cli/debug/channel.ts
|
|
40
34
|
const NOOP_CLOSE = async () => {};
|
|
@@ -144,7 +138,469 @@ function createDebugChannel(options) {
|
|
|
144
138
|
}
|
|
145
139
|
};
|
|
146
140
|
}
|
|
147
|
-
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/cli/program/version-embedded.ts
|
|
143
|
+
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
144
|
+
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
145
|
+
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);
|
|
146
|
+
let formatter = (open, close, replace = open) => (input) => {
|
|
147
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
148
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
149
|
+
};
|
|
150
|
+
let replaceClose = (string, close, replace, index) => {
|
|
151
|
+
let result = "", cursor = 0;
|
|
152
|
+
do {
|
|
153
|
+
result += string.substring(cursor, index) + replace;
|
|
154
|
+
cursor = index + close.length;
|
|
155
|
+
index = string.indexOf(close, cursor);
|
|
156
|
+
} while (~index);
|
|
157
|
+
return result + string.substring(cursor);
|
|
158
|
+
};
|
|
159
|
+
let createColors = (enabled = isColorSupported) => {
|
|
160
|
+
let f = enabled ? formatter : () => String;
|
|
161
|
+
return {
|
|
162
|
+
isColorSupported: enabled,
|
|
163
|
+
reset: f("\x1B[0m", "\x1B[0m"),
|
|
164
|
+
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
165
|
+
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
166
|
+
italic: f("\x1B[3m", "\x1B[23m"),
|
|
167
|
+
underline: f("\x1B[4m", "\x1B[24m"),
|
|
168
|
+
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
169
|
+
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
170
|
+
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
171
|
+
black: f("\x1B[30m", "\x1B[39m"),
|
|
172
|
+
red: f("\x1B[31m", "\x1B[39m"),
|
|
173
|
+
green: f("\x1B[32m", "\x1B[39m"),
|
|
174
|
+
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
175
|
+
blue: f("\x1B[34m", "\x1B[39m"),
|
|
176
|
+
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
177
|
+
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
178
|
+
white: f("\x1B[37m", "\x1B[39m"),
|
|
179
|
+
gray: f("\x1B[90m", "\x1B[39m"),
|
|
180
|
+
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
181
|
+
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
182
|
+
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
183
|
+
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
184
|
+
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
185
|
+
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
186
|
+
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
187
|
+
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
188
|
+
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
189
|
+
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
190
|
+
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
191
|
+
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
192
|
+
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
193
|
+
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
194
|
+
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
195
|
+
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
196
|
+
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
197
|
+
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
198
|
+
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
199
|
+
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
200
|
+
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
201
|
+
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
202
|
+
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
203
|
+
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
module.exports = createColors();
|
|
207
|
+
module.exports.createColors = createColors;
|
|
208
|
+
})))(), 1);
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/cli/batch/jobs/limits.ts
|
|
211
|
+
const DEFAULT_UV_THREADPOOL_SIZE = 4;
|
|
212
|
+
function parsePositiveInteger(value) {
|
|
213
|
+
if (!value) return;
|
|
214
|
+
const parsed = Number.parseInt(value, 10);
|
|
215
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return;
|
|
216
|
+
return parsed;
|
|
217
|
+
}
|
|
218
|
+
function resolveBatchJobsLimit(env = process.env) {
|
|
219
|
+
const cpuLimit = Math.max(1, os.availableParallelism());
|
|
220
|
+
const uvThreadpool = parsePositiveInteger(env.UV_THREADPOOL_SIZE) ?? DEFAULT_UV_THREADPOOL_SIZE;
|
|
221
|
+
const ioLimit = Math.max(1, uvThreadpool * 2);
|
|
222
|
+
return {
|
|
223
|
+
suggestedMaxJobs: Math.max(1, Math.min(cpuLimit, ioLimit)),
|
|
224
|
+
cpuLimit,
|
|
225
|
+
uvThreadpool,
|
|
226
|
+
ioLimit
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function clampRequestedJobs(requestedJobs, limits) {
|
|
230
|
+
return Math.max(1, Math.min(requestedJobs, limits.suggestedMaxJobs));
|
|
231
|
+
}
|
|
232
|
+
function formatJobsAdvisoryWarning(requestedJobs, effectiveJobs, limits) {
|
|
233
|
+
return [
|
|
234
|
+
`Warning: requested --jobs=${requestedJobs} exceeds suggested host limit (${limits.suggestedMaxJobs}).`,
|
|
235
|
+
`Running with --jobs=${effectiveJobs} as a safety cap.`,
|
|
236
|
+
`Host limits: cpuLimit=${limits.cpuLimit}, uvThreadpool=${limits.uvThreadpool}, ioLimit=${limits.ioLimit}.`
|
|
237
|
+
].join(" ");
|
|
238
|
+
}
|
|
239
|
+
function isResourceLimitError(error) {
|
|
240
|
+
if (typeof error !== "object" || error === null) return false;
|
|
241
|
+
const code = "code" in error ? error.code : void 0;
|
|
242
|
+
return code === "EMFILE" || code === "ENFILE";
|
|
243
|
+
}
|
|
244
|
+
function createResourceLimitError(path, error, requestedJobs, limits) {
|
|
245
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
246
|
+
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
|
|
247
|
+
return new Error([
|
|
248
|
+
`Resource limit reached while processing: ${path} (${code}: ${message}).`,
|
|
249
|
+
`Requested --jobs=${requestedJobs}; suggested host limit is ${limits.suggestedMaxJobs}.`,
|
|
250
|
+
"Reduce --jobs or raise OS file descriptor limits before retrying."
|
|
251
|
+
].join(" "));
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/cli/batch/jobs/load-count-worker.ts
|
|
255
|
+
var WorkerRouteUnavailableError = class extends Error {};
|
|
256
|
+
async function resolveWorkerThreadsAvailability() {
|
|
257
|
+
try {
|
|
258
|
+
return typeof (await import("node:worker_threads")).Worker === "function";
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function isFallbackFriendlyWorkerError(error) {
|
|
264
|
+
if (typeof error !== "object" || error === null) return false;
|
|
265
|
+
const code = "code" in error ? String(error.code) : "";
|
|
266
|
+
if (code === "ERR_WORKER_PATH" || code === "ERR_WORKER_UNSUPPORTED_EXTENSION" || code === "ERR_UNKNOWN_FILE_EXTENSION" || code === "ERR_MODULE_NOT_FOUND") return true;
|
|
267
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
268
|
+
return message.includes("Unknown file extension") || message.includes("Cannot find module");
|
|
269
|
+
}
|
|
270
|
+
async function resolveWorkerRoutePreflight(env = process.env) {
|
|
271
|
+
const disableWorkerJobsEnv = env.WORD_COUNTER_DISABLE_WORKER_JOBS ?? null;
|
|
272
|
+
const workerRouteDisabledByEnv = disableWorkerJobsEnv === "1";
|
|
273
|
+
const workerThreadsAvailable = await resolveWorkerThreadsAvailability();
|
|
274
|
+
try {
|
|
275
|
+
return {
|
|
276
|
+
workerThreadsAvailable,
|
|
277
|
+
workerRouteDisabledByEnv,
|
|
278
|
+
disableWorkerJobsEnv,
|
|
279
|
+
workerPoolModuleLoadable: true,
|
|
280
|
+
workerEntryFound: (await import("./worker-pool.mjs")).resolveWorkerEntryUrl() !== null
|
|
281
|
+
};
|
|
282
|
+
} catch {
|
|
283
|
+
return {
|
|
284
|
+
workerThreadsAvailable,
|
|
285
|
+
workerRouteDisabledByEnv,
|
|
286
|
+
disableWorkerJobsEnv,
|
|
287
|
+
workerPoolModuleLoadable: false,
|
|
288
|
+
workerEntryFound: false
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async function countBatchInputsWithWorkerJobs(filePaths, options) {
|
|
293
|
+
if (process.env.WORD_COUNTER_DISABLE_WORKER_JOBS === "1") throw new WorkerRouteUnavailableError("Worker route disabled by environment.");
|
|
294
|
+
let workerPoolModule;
|
|
295
|
+
try {
|
|
296
|
+
workerPoolModule = await import("./worker-pool.mjs");
|
|
297
|
+
} catch (error) {
|
|
298
|
+
throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
return await workerPoolModule.countBatchInputsWithWorkerPool({
|
|
302
|
+
filePaths,
|
|
303
|
+
jobs: options.jobs,
|
|
304
|
+
section: options.section,
|
|
305
|
+
wcOptions: options.wcOptions,
|
|
306
|
+
preserveCollectorSegments: options.preserveCollectorSegments,
|
|
307
|
+
onFileProcessed: options.onFileProcessed
|
|
308
|
+
});
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (error instanceof workerPoolModule.WorkerPoolTaskFatalError) {
|
|
311
|
+
if (error.code === "EMFILE" || error.code === "ENFILE") throw createResourceLimitError(error.path, {
|
|
312
|
+
code: error.code,
|
|
313
|
+
message: error.message
|
|
314
|
+
}, options.jobs, resolveBatchJobsLimit());
|
|
315
|
+
throw new Error(error.message);
|
|
316
|
+
}
|
|
317
|
+
if (error instanceof workerPoolModule.WorkerPoolUnavailableError || isFallbackFriendlyWorkerError(error)) throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/cli/doctor/checks.ts
|
|
323
|
+
const REQUIRED_NODE_RANGE = ">=20";
|
|
324
|
+
const REQUIRED_NODE_MAJOR = 20;
|
|
325
|
+
const SAMPLE_TEXT = "Hello 世界";
|
|
326
|
+
function normalizePackageVersion(value) {
|
|
327
|
+
const trimmed = value?.trim();
|
|
328
|
+
return trimmed && trimmed.length > 0 ? trimmed : "0.0.0";
|
|
329
|
+
}
|
|
330
|
+
function deriveBuildChannel(packageVersion) {
|
|
331
|
+
const prereleaseMatch = /(?:^|[.-])(alpha|beta|rc|canary)(?:[.-]|$)/i.exec(packageVersion);
|
|
332
|
+
if (!prereleaseMatch) return "stable";
|
|
333
|
+
const channel = prereleaseMatch[1]?.toLowerCase();
|
|
334
|
+
if (channel === "alpha" || channel === "beta" || channel === "rc" || channel === "canary") return channel;
|
|
335
|
+
return "stable";
|
|
336
|
+
}
|
|
337
|
+
function parseNodeMajor(version) {
|
|
338
|
+
const match = /^v?(\d+)(?:\.\d+){0,2}(?:[-+].*)?$/.exec(version.trim());
|
|
339
|
+
if (!match) return null;
|
|
340
|
+
const major = Number.parseInt(match[1] ?? "", 10);
|
|
341
|
+
return Number.isFinite(major) ? major : null;
|
|
342
|
+
}
|
|
343
|
+
function resolveRuntimeSummary(overrides = {}) {
|
|
344
|
+
const packageVersion = normalizePackageVersion(overrides.packageVersion ?? "0.1.5-canary.1");
|
|
345
|
+
const nodeVersion = overrides.nodeVersion ?? process.version;
|
|
346
|
+
const major = parseNodeMajor(nodeVersion);
|
|
347
|
+
return {
|
|
348
|
+
packageVersion,
|
|
349
|
+
buildChannel: deriveBuildChannel(packageVersion),
|
|
350
|
+
requiredNodeRange: REQUIRED_NODE_RANGE,
|
|
351
|
+
nodeVersion,
|
|
352
|
+
meetsProjectRequirement: major !== null && major >= REQUIRED_NODE_MAJOR,
|
|
353
|
+
platform: overrides.platform ?? process.platform,
|
|
354
|
+
arch: overrides.arch ?? process.arch
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function resolveSegmenterHealth(overrides = {}) {
|
|
358
|
+
const Segmenter = (overrides.intl ?? Intl).Segmenter;
|
|
359
|
+
const available = typeof Segmenter === "function";
|
|
360
|
+
let wordGranularity = false;
|
|
361
|
+
let graphemeGranularity = false;
|
|
362
|
+
let sampleWordSegmentation = false;
|
|
363
|
+
if (!available) return {
|
|
364
|
+
available,
|
|
365
|
+
wordGranularity,
|
|
366
|
+
graphemeGranularity,
|
|
367
|
+
sampleWordSegmentation
|
|
368
|
+
};
|
|
369
|
+
try {
|
|
370
|
+
const wordSegmenter = new Segmenter("en", { granularity: "word" });
|
|
371
|
+
wordGranularity = true;
|
|
372
|
+
for (const _segment of wordSegmenter.segment(SAMPLE_TEXT)) {
|
|
373
|
+
sampleWordSegmentation = true;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
wordGranularity = false;
|
|
378
|
+
sampleWordSegmentation = false;
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
new Segmenter("en", { granularity: "grapheme" });
|
|
382
|
+
graphemeGranularity = true;
|
|
383
|
+
} catch {
|
|
384
|
+
graphemeGranularity = false;
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
available,
|
|
388
|
+
wordGranularity,
|
|
389
|
+
graphemeGranularity,
|
|
390
|
+
sampleWordSegmentation
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function collectWarnings(runtime, segmenter, workerRoute) {
|
|
394
|
+
const warnings = [];
|
|
395
|
+
if (!runtime.meetsProjectRequirement) warnings.push(`Node.js ${runtime.nodeVersion} is outside the supported range ${runtime.requiredNodeRange}.`);
|
|
396
|
+
if (!segmenter.available) warnings.push("Intl.Segmenter is unavailable.");
|
|
397
|
+
else {
|
|
398
|
+
if (!segmenter.wordGranularity) warnings.push("Intl.Segmenter word granularity is unusable.");
|
|
399
|
+
if (!segmenter.graphemeGranularity) warnings.push("Intl.Segmenter grapheme granularity is unusable.");
|
|
400
|
+
if (!segmenter.sampleWordSegmentation) warnings.push("Intl.Segmenter sample segmentation failed.");
|
|
401
|
+
}
|
|
402
|
+
if (!workerRoute.workerThreadsAvailable) warnings.push("Worker threads are unavailable on this runtime.");
|
|
403
|
+
if (workerRoute.workerRouteDisabledByEnv) warnings.push("Worker route is disabled by environment.");
|
|
404
|
+
if (!workerRoute.workerPoolModuleLoadable) warnings.push("Worker route preflight failed: worker-pool module could not be loaded.");
|
|
405
|
+
else if (!workerRoute.workerEntryFound) warnings.push("Worker route preflight failed: count-worker entry file was not found.");
|
|
406
|
+
return warnings;
|
|
407
|
+
}
|
|
408
|
+
function resolveStatus(segmenter, warnings) {
|
|
409
|
+
if (!segmenter.available || !segmenter.wordGranularity || !segmenter.graphemeGranularity || !segmenter.sampleWordSegmentation) return "fail";
|
|
410
|
+
if (warnings.length > 0) return "warn";
|
|
411
|
+
return "ok";
|
|
412
|
+
}
|
|
413
|
+
async function createDoctorReport(overrides = {}) {
|
|
414
|
+
const runtime = resolveRuntimeSummary(overrides);
|
|
415
|
+
const segmenter = resolveSegmenterHealth(overrides);
|
|
416
|
+
const env = overrides.env ?? process.env;
|
|
417
|
+
const jobs = resolveBatchJobsLimit(env);
|
|
418
|
+
const workerRoute = await resolveWorkerRoutePreflight(env);
|
|
419
|
+
const warnings = collectWarnings(runtime, segmenter, workerRoute);
|
|
420
|
+
return {
|
|
421
|
+
status: resolveStatus(segmenter, warnings),
|
|
422
|
+
runtime,
|
|
423
|
+
segmenter,
|
|
424
|
+
jobs,
|
|
425
|
+
workerRoute,
|
|
426
|
+
warnings
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/cli/doctor/render.ts
|
|
431
|
+
function colorStatus(status) {
|
|
432
|
+
if (status === "ok") return import_picocolors.default.green(import_picocolors.default.bold(status));
|
|
433
|
+
if (status === "warn") return import_picocolors.default.yellow(import_picocolors.default.bold(status));
|
|
434
|
+
return import_picocolors.default.red(import_picocolors.default.bold(status));
|
|
435
|
+
}
|
|
436
|
+
function renderSection(title, lines) {
|
|
437
|
+
console.log(import_picocolors.default.bold(title));
|
|
438
|
+
for (const line of lines) console.log(`- ${line}`);
|
|
439
|
+
console.log("");
|
|
440
|
+
}
|
|
441
|
+
function colorBoolean(value, yes = "yes", no = "no") {
|
|
442
|
+
return value ? import_picocolors.default.green(yes) : import_picocolors.default.red(no);
|
|
443
|
+
}
|
|
444
|
+
function colorNumber(value) {
|
|
445
|
+
return import_picocolors.default.yellow(String(value));
|
|
446
|
+
}
|
|
447
|
+
function colorStatusWord(value) {
|
|
448
|
+
return value ? import_picocolors.default.green("ok") : import_picocolors.default.red("fail");
|
|
449
|
+
}
|
|
450
|
+
function renderStandardDoctorReport(report) {
|
|
451
|
+
console.log(`Doctor: ${colorStatus(report.status)}`);
|
|
452
|
+
console.log("");
|
|
453
|
+
renderSection("Runtime", [
|
|
454
|
+
`package: ${report.runtime.packageVersion} (${report.runtime.buildChannel})`,
|
|
455
|
+
`node: ${report.runtime.nodeVersion} (supported: ${colorBoolean(report.runtime.meetsProjectRequirement)}; required ${report.runtime.requiredNodeRange})`,
|
|
456
|
+
`platform: ${report.runtime.platform} ${report.runtime.arch}`
|
|
457
|
+
]);
|
|
458
|
+
renderSection("Segmenter", [
|
|
459
|
+
`Intl.Segmenter: ${colorBoolean(report.segmenter.available, "available", "missing")}`,
|
|
460
|
+
`word granularity: ${colorStatusWord(report.segmenter.wordGranularity)}`,
|
|
461
|
+
`grapheme granularity: ${colorStatusWord(report.segmenter.graphemeGranularity)}`,
|
|
462
|
+
`sample segmentation: ${colorStatusWord(report.segmenter.sampleWordSegmentation)}`
|
|
463
|
+
]);
|
|
464
|
+
renderSection("Batch jobs", [
|
|
465
|
+
`cpuLimit: ${colorNumber(report.jobs.cpuLimit)}`,
|
|
466
|
+
`uvThreadpool: ${colorNumber(report.jobs.uvThreadpool)}`,
|
|
467
|
+
`ioLimit: ${colorNumber(report.jobs.ioLimit)}`,
|
|
468
|
+
`suggestedMaxJobs: ${colorNumber(report.jobs.suggestedMaxJobs)}`
|
|
469
|
+
]);
|
|
470
|
+
renderSection("Worker route", [
|
|
471
|
+
`worker threads: ${colorBoolean(report.workerRoute.workerThreadsAvailable, "available", "missing")}`,
|
|
472
|
+
`disabled by env: ${colorBoolean(report.workerRoute.workerRouteDisabledByEnv)}`,
|
|
473
|
+
`disableWorkerJobsEnv: ${report.workerRoute.disableWorkerJobsEnv ?? "null"}`,
|
|
474
|
+
`worker pool module: ${colorBoolean(report.workerRoute.workerPoolModuleLoadable, "loadable", "missing")}`,
|
|
475
|
+
`worker entry: ${colorBoolean(report.workerRoute.workerEntryFound, "found", "missing")}`
|
|
476
|
+
]);
|
|
477
|
+
if (report.warnings.length > 0) {
|
|
478
|
+
console.log(import_picocolors.default.bold("Warnings"));
|
|
479
|
+
for (const warning of report.warnings) console.log(import_picocolors.default.yellow(`- ${warning}`));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function renderDoctorReport(report, options) {
|
|
483
|
+
if (options.format === "json") {
|
|
484
|
+
console.log(JSON.stringify(report, null, options.pretty ? 2 : 0));
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
renderStandardDoctorReport(report);
|
|
488
|
+
}
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region src/cli/doctor/run.ts
|
|
491
|
+
const DOCTOR_HELP_LINES = [
|
|
492
|
+
"Usage: word-counter doctor [options]",
|
|
493
|
+
"",
|
|
494
|
+
"report runtime diagnostics for this host",
|
|
495
|
+
"",
|
|
496
|
+
"Options:",
|
|
497
|
+
" --format <format> doctor output format (json)",
|
|
498
|
+
" --pretty pretty print doctor JSON output (default: false)",
|
|
499
|
+
" -h, --help display help for command"
|
|
500
|
+
];
|
|
501
|
+
function parseDoctorFormat(rawValue) {
|
|
502
|
+
if (rawValue === void 0) return "standard";
|
|
503
|
+
if (rawValue === "json") return "json";
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
function validateDoctorInvocation(argv) {
|
|
507
|
+
const doctorIndex = argv.findIndex((token, index) => index >= 2 && token === "doctor");
|
|
508
|
+
const tokens = doctorIndex >= 0 ? argv.slice(doctorIndex + 1) : [];
|
|
509
|
+
let expectsFormatValue = false;
|
|
510
|
+
let format = "standard";
|
|
511
|
+
let pretty = false;
|
|
512
|
+
for (const token of tokens) {
|
|
513
|
+
if (token === "-h" || token === "--help") return {
|
|
514
|
+
ok: true,
|
|
515
|
+
help: true
|
|
516
|
+
};
|
|
517
|
+
if (expectsFormatValue) {
|
|
518
|
+
const parsedFormat = parseDoctorFormat(token);
|
|
519
|
+
if (parsedFormat === null) return {
|
|
520
|
+
ok: false,
|
|
521
|
+
message: "`doctor` only supports default text output or `--format json`."
|
|
522
|
+
};
|
|
523
|
+
format = parsedFormat;
|
|
524
|
+
expectsFormatValue = false;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
if (token === "--") return {
|
|
528
|
+
ok: false,
|
|
529
|
+
message: "`doctor` does not accept positional inputs."
|
|
530
|
+
};
|
|
531
|
+
if (token === "--format") {
|
|
532
|
+
expectsFormatValue = true;
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
if (token.startsWith("--format=")) {
|
|
536
|
+
const rawValue = token.slice(9);
|
|
537
|
+
if (rawValue.length === 0) return {
|
|
538
|
+
ok: false,
|
|
539
|
+
message: "`--format` requires a value."
|
|
540
|
+
};
|
|
541
|
+
const parsedFormat = parseDoctorFormat(rawValue);
|
|
542
|
+
if (parsedFormat === null) return {
|
|
543
|
+
ok: false,
|
|
544
|
+
message: "`doctor` only supports default text output or `--format json`."
|
|
545
|
+
};
|
|
546
|
+
format = parsedFormat;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (token === "--pretty") {
|
|
550
|
+
pretty = true;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (token.startsWith("-")) return {
|
|
554
|
+
ok: false,
|
|
555
|
+
message: `\`${token}\` is not supported by \`doctor\`.`
|
|
556
|
+
};
|
|
557
|
+
return {
|
|
558
|
+
ok: false,
|
|
559
|
+
message: "`doctor` does not accept positional inputs."
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
if (expectsFormatValue) return {
|
|
563
|
+
ok: false,
|
|
564
|
+
message: "`--format` requires a value."
|
|
565
|
+
};
|
|
566
|
+
if (pretty && format !== "json") return {
|
|
567
|
+
ok: false,
|
|
568
|
+
message: "`--pretty` requires `--format json`."
|
|
569
|
+
};
|
|
570
|
+
return {
|
|
571
|
+
ok: true,
|
|
572
|
+
format,
|
|
573
|
+
pretty
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function isExplicitDoctorInvocation(argv) {
|
|
577
|
+
if (argv[2] !== "doctor") return false;
|
|
578
|
+
const trailingTokens = argv.slice(3);
|
|
579
|
+
if (trailingTokens.length === 0) return true;
|
|
580
|
+
return trailingTokens.some((token) => token === "--" || token.startsWith("-"));
|
|
581
|
+
}
|
|
582
|
+
function printDoctorHelp() {
|
|
583
|
+
for (const line of DOCTOR_HELP_LINES) console.log(line);
|
|
584
|
+
}
|
|
585
|
+
async function executeDoctorCommand({ argv, runtime }) {
|
|
586
|
+
const validated = validateDoctorInvocation(argv);
|
|
587
|
+
if (!validated.ok) {
|
|
588
|
+
console.error(import_picocolors.default.red(`error: ${validated.message}`));
|
|
589
|
+
process.exitCode = 1;
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if ("help" in validated) {
|
|
593
|
+
printDoctorHelp();
|
|
594
|
+
process.exitCode = 0;
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const report = await createDoctorReport(runtime);
|
|
598
|
+
renderDoctorReport(report, {
|
|
599
|
+
format: validated.format,
|
|
600
|
+
pretty: validated.pretty
|
|
601
|
+
});
|
|
602
|
+
process.exitCode = report.status === "fail" ? 2 : 0;
|
|
603
|
+
}
|
|
148
604
|
//#endregion
|
|
149
605
|
//#region src/cli/path/filter.ts
|
|
150
606
|
const DEFAULT_INCLUDE_EXTENSIONS = Object.freeze([
|
|
@@ -221,7 +677,6 @@ function shouldIncludeFromDirectoryRegex(relativePath, filter) {
|
|
|
221
677
|
if (!filter.regex) return true;
|
|
222
678
|
return filter.regex.test(relativePath);
|
|
223
679
|
}
|
|
224
|
-
|
|
225
680
|
//#endregion
|
|
226
681
|
//#region src/cli/total-of.ts
|
|
227
682
|
const TOTAL_OF_PARTS = Object.freeze([
|
|
@@ -316,7 +771,6 @@ function resolveTotalOfOverride(result, parts) {
|
|
|
316
771
|
function formatTotalOfParts(parts) {
|
|
317
772
|
return parts.join(", ");
|
|
318
773
|
}
|
|
319
|
-
|
|
320
774
|
//#endregion
|
|
321
775
|
//#region src/cli/program/options.ts
|
|
322
776
|
const MODE_CHOICES = [
|
|
@@ -355,81 +809,6 @@ function parseJobsOption(value) {
|
|
|
355
809
|
function configureProgramOptions(program, parseMode) {
|
|
356
810
|
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: 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("--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();
|
|
357
811
|
}
|
|
358
|
-
|
|
359
|
-
//#endregion
|
|
360
|
-
//#region node_modules/picocolors/picocolors.js
|
|
361
|
-
var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
362
|
-
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
363
|
-
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);
|
|
364
|
-
let formatter = (open, close, replace = open) => (input) => {
|
|
365
|
-
let string = "" + input, index = string.indexOf(close, open.length);
|
|
366
|
-
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
367
|
-
};
|
|
368
|
-
let replaceClose = (string, close, replace, index) => {
|
|
369
|
-
let result = "", cursor = 0;
|
|
370
|
-
do {
|
|
371
|
-
result += string.substring(cursor, index) + replace;
|
|
372
|
-
cursor = index + close.length;
|
|
373
|
-
index = string.indexOf(close, cursor);
|
|
374
|
-
} while (~index);
|
|
375
|
-
return result + string.substring(cursor);
|
|
376
|
-
};
|
|
377
|
-
let createColors = (enabled = isColorSupported) => {
|
|
378
|
-
let f = enabled ? formatter : () => String;
|
|
379
|
-
return {
|
|
380
|
-
isColorSupported: enabled,
|
|
381
|
-
reset: f("\x1B[0m", "\x1B[0m"),
|
|
382
|
-
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
383
|
-
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
384
|
-
italic: f("\x1B[3m", "\x1B[23m"),
|
|
385
|
-
underline: f("\x1B[4m", "\x1B[24m"),
|
|
386
|
-
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
387
|
-
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
388
|
-
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
389
|
-
black: f("\x1B[30m", "\x1B[39m"),
|
|
390
|
-
red: f("\x1B[31m", "\x1B[39m"),
|
|
391
|
-
green: f("\x1B[32m", "\x1B[39m"),
|
|
392
|
-
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
393
|
-
blue: f("\x1B[34m", "\x1B[39m"),
|
|
394
|
-
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
395
|
-
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
396
|
-
white: f("\x1B[37m", "\x1B[39m"),
|
|
397
|
-
gray: f("\x1B[90m", "\x1B[39m"),
|
|
398
|
-
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
399
|
-
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
400
|
-
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
401
|
-
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
402
|
-
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
403
|
-
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
404
|
-
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
405
|
-
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
406
|
-
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
407
|
-
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
408
|
-
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
409
|
-
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
410
|
-
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
411
|
-
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
412
|
-
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
413
|
-
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
414
|
-
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
415
|
-
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
416
|
-
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
417
|
-
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
418
|
-
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
419
|
-
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
420
|
-
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
421
|
-
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
422
|
-
};
|
|
423
|
-
};
|
|
424
|
-
module.exports = createColors();
|
|
425
|
-
module.exports.createColors = createColors;
|
|
426
|
-
}));
|
|
427
|
-
|
|
428
|
-
//#endregion
|
|
429
|
-
//#region src/cli/program/version-embedded.ts
|
|
430
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
431
|
-
const EMBEDDED_PACKAGE_VERSION = "0.1.4";
|
|
432
|
-
|
|
433
812
|
//#endregion
|
|
434
813
|
//#region src/cli/program/version.ts
|
|
435
814
|
function* candidateSearchRoots() {
|
|
@@ -462,7 +841,7 @@ function normalizeVersion(value) {
|
|
|
462
841
|
return trimmed;
|
|
463
842
|
}
|
|
464
843
|
function resolvePackageVersion(options = {}) {
|
|
465
|
-
const embeddedVersion = normalizeVersion(options.embeddedVersion ??
|
|
844
|
+
const embeddedVersion = normalizeVersion(options.embeddedVersion ?? "0.1.5-canary.1");
|
|
466
845
|
if (embeddedVersion) return embeddedVersion;
|
|
467
846
|
const maxLevels = options.maxLevels ?? 8;
|
|
468
847
|
const resolveFromPath = options.resolveFromPath ?? resolveVersionFromPath;
|
|
@@ -480,58 +859,11 @@ function getFormattedVersionLabel() {
|
|
|
480
859
|
const version = resolvePackageVersion();
|
|
481
860
|
return import_picocolors.default.bgBlack(import_picocolors.default.bold(import_picocolors.default.italic(` word-counter ${import_picocolors.default.cyanBright(`ver.${version}`)} `)));
|
|
482
861
|
}
|
|
483
|
-
|
|
484
|
-
//#endregion
|
|
485
|
-
//#region src/cli/batch/jobs/limits.ts
|
|
486
|
-
const DEFAULT_UV_THREADPOOL_SIZE = 4;
|
|
487
|
-
function parsePositiveInteger(value) {
|
|
488
|
-
if (!value) return;
|
|
489
|
-
const parsed = Number.parseInt(value, 10);
|
|
490
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return;
|
|
491
|
-
return parsed;
|
|
492
|
-
}
|
|
493
|
-
function resolveBatchJobsLimit(env = process.env) {
|
|
494
|
-
const cpuLimit = Math.max(1, os.availableParallelism());
|
|
495
|
-
const uvThreadpool = parsePositiveInteger(env.UV_THREADPOOL_SIZE) ?? DEFAULT_UV_THREADPOOL_SIZE;
|
|
496
|
-
const ioLimit = Math.max(1, uvThreadpool * 2);
|
|
497
|
-
return {
|
|
498
|
-
suggestedMaxJobs: Math.max(1, Math.min(cpuLimit, ioLimit)),
|
|
499
|
-
cpuLimit,
|
|
500
|
-
uvThreadpool,
|
|
501
|
-
ioLimit
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
function clampRequestedJobs(requestedJobs, limits) {
|
|
505
|
-
return Math.max(1, Math.min(requestedJobs, limits.suggestedMaxJobs));
|
|
506
|
-
}
|
|
507
|
-
function formatJobsAdvisoryWarning(requestedJobs, effectiveJobs, limits) {
|
|
508
|
-
return [
|
|
509
|
-
`Warning: requested --jobs=${requestedJobs} exceeds suggested host limit (${limits.suggestedMaxJobs}).`,
|
|
510
|
-
`Running with --jobs=${effectiveJobs} as a safety cap.`,
|
|
511
|
-
`Host limits: cpuLimit=${limits.cpuLimit}, uvThreadpool=${limits.uvThreadpool}, ioLimit=${limits.ioLimit}.`
|
|
512
|
-
].join(" ");
|
|
513
|
-
}
|
|
514
|
-
function isResourceLimitError(error) {
|
|
515
|
-
if (typeof error !== "object" || error === null) return false;
|
|
516
|
-
const code = "code" in error ? error.code : void 0;
|
|
517
|
-
return code === "EMFILE" || code === "ENFILE";
|
|
518
|
-
}
|
|
519
|
-
function createResourceLimitError(path, error, requestedJobs, limits) {
|
|
520
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
521
|
-
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
|
|
522
|
-
return new Error([
|
|
523
|
-
`Resource limit reached while processing: ${path} (${code}: ${message}).`,
|
|
524
|
-
`Requested --jobs=${requestedJobs}; suggested host limit is ${limits.suggestedMaxJobs}.`,
|
|
525
|
-
"Reduce --jobs or raise OS file descriptor limits before retrying."
|
|
526
|
-
].join(" "));
|
|
527
|
-
}
|
|
528
|
-
|
|
529
862
|
//#endregion
|
|
530
863
|
//#region src/utils/append-all.ts
|
|
531
864
|
function appendAll(target, source) {
|
|
532
865
|
for (const item of source) target.push(item);
|
|
533
866
|
}
|
|
534
|
-
|
|
535
867
|
//#endregion
|
|
536
868
|
//#region src/markdown/toml/arrays.ts
|
|
537
869
|
function ensureArrayContainer(result, key) {
|
|
@@ -547,7 +879,6 @@ function flattenArrayTables(result) {
|
|
|
547
879
|
result[key] = value.map((entry) => Object.entries(entry).map(([entryKey, entryValue]) => `${entryKey}=${entryValue}`).join(", ")).join(" | ");
|
|
548
880
|
}
|
|
549
881
|
}
|
|
550
|
-
|
|
551
882
|
//#endregion
|
|
552
883
|
//#region src/markdown/toml/keys.ts
|
|
553
884
|
function stripKeyQuotes(key) {
|
|
@@ -566,7 +897,6 @@ function normalizeKeyPath(key) {
|
|
|
566
897
|
if (segments.some((segment) => !segment)) return null;
|
|
567
898
|
return segments.join(".");
|
|
568
899
|
}
|
|
569
|
-
|
|
570
900
|
//#endregion
|
|
571
901
|
//#region src/markdown/toml/strings.ts
|
|
572
902
|
function stripInlineComment(line) {
|
|
@@ -615,7 +945,6 @@ function parseStringLiteral(value) {
|
|
|
615
945
|
if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
616
946
|
return null;
|
|
617
947
|
}
|
|
618
|
-
|
|
619
948
|
//#endregion
|
|
620
949
|
//#region src/markdown/toml/values.ts
|
|
621
950
|
function parsePrimitive(raw) {
|
|
@@ -773,7 +1102,6 @@ function toPlainText(value) {
|
|
|
773
1102
|
if (Array.isArray(value)) return value.map((item) => String(item)).join(", ");
|
|
774
1103
|
return String(value);
|
|
775
1104
|
}
|
|
776
|
-
|
|
777
1105
|
//#endregion
|
|
778
1106
|
//#region src/markdown/toml/parse-frontmatter.ts
|
|
779
1107
|
function parseTomlFrontmatter(frontmatter) {
|
|
@@ -857,7 +1185,6 @@ function parseTomlFrontmatter(frontmatter) {
|
|
|
857
1185
|
flattenArrayTables(result);
|
|
858
1186
|
return result;
|
|
859
1187
|
}
|
|
860
|
-
|
|
861
1188
|
//#endregion
|
|
862
1189
|
//#region src/markdown/parse-markdown.ts
|
|
863
1190
|
const FENCE_TO_TYPE = {
|
|
@@ -992,7 +1319,6 @@ function parseMarkdown(input) {
|
|
|
992
1319
|
frontmatterType: openingType
|
|
993
1320
|
};
|
|
994
1321
|
}
|
|
995
|
-
|
|
996
1322
|
//#endregion
|
|
997
1323
|
//#region src/wc/segmenter.ts
|
|
998
1324
|
const segmenterCache = /* @__PURE__ */ new Map();
|
|
@@ -1021,7 +1347,6 @@ function countCharsForLocale(text, locale) {
|
|
|
1021
1347
|
for (const _segment of segmenter.segment(text)) count++;
|
|
1022
1348
|
return count;
|
|
1023
1349
|
}
|
|
1024
|
-
|
|
1025
1350
|
//#endregion
|
|
1026
1351
|
//#region src/wc/non-words.ts
|
|
1027
1352
|
const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation})/u;
|
|
@@ -1135,7 +1460,6 @@ function createWhitespaceCounts() {
|
|
|
1135
1460
|
other: 0
|
|
1136
1461
|
};
|
|
1137
1462
|
}
|
|
1138
|
-
|
|
1139
1463
|
//#endregion
|
|
1140
1464
|
//#region src/wc/analyze.ts
|
|
1141
1465
|
function analyzeChunk(chunk, collectNonWords, includeWhitespace) {
|
|
@@ -1235,7 +1559,6 @@ function aggregateByLocale(chunks) {
|
|
|
1235
1559
|
}
|
|
1236
1560
|
return order.map((locale) => map.get(locale));
|
|
1237
1561
|
}
|
|
1238
|
-
|
|
1239
1562
|
//#endregion
|
|
1240
1563
|
//#region src/wc/mode.ts
|
|
1241
1564
|
const MODE_ALIASES = {
|
|
@@ -1303,10 +1626,7 @@ function normalizeMode(input) {
|
|
|
1303
1626
|
function resolveMode(input, fallback = "chunk") {
|
|
1304
1627
|
return normalizeMode(input) ?? fallback;
|
|
1305
1628
|
}
|
|
1306
|
-
|
|
1307
|
-
//#endregion
|
|
1308
|
-
//#region src/wc/latin-hints.ts
|
|
1309
|
-
const DEFAULT_LATIN_HINT_RULES_SOURCE = [
|
|
1629
|
+
const DEFAULT_LATIN_HINT_RULES = Object.freeze([
|
|
1310
1630
|
{
|
|
1311
1631
|
tag: "de",
|
|
1312
1632
|
pattern: "[äöüÄÖÜß]"
|
|
@@ -1343,13 +1663,10 @@ const DEFAULT_LATIN_HINT_RULES_SOURCE = [
|
|
|
1343
1663
|
tag: "is",
|
|
1344
1664
|
pattern: "[ðÐþÞ]"
|
|
1345
1665
|
}
|
|
1346
|
-
];
|
|
1347
|
-
const DEFAULT_LATIN_HINT_RULES = Object.freeze(DEFAULT_LATIN_HINT_RULES_SOURCE.map((rule) => Object.freeze({ ...rule })));
|
|
1348
|
-
|
|
1666
|
+
].map((rule) => Object.freeze({ ...rule })));
|
|
1349
1667
|
//#endregion
|
|
1350
1668
|
//#region src/wc/locale-detect.ts
|
|
1351
1669
|
const DEFAULT_LOCALE = "und-Latn";
|
|
1352
|
-
const DEFAULT_HAN_TAG = "und-Hani";
|
|
1353
1670
|
const MAX_LATIN_HINT_PATTERN_LENGTH = 256;
|
|
1354
1671
|
const regex = {
|
|
1355
1672
|
hiragana: /\p{Script=Hiragana}/u,
|
|
@@ -1465,18 +1782,17 @@ function detectLocaleForChar(char, previousLocale, options = {}, context = resol
|
|
|
1465
1782
|
if (regex.thai.test(char)) return "th";
|
|
1466
1783
|
if (regex.han.test(char)) {
|
|
1467
1784
|
if (allowJapaneseHanCarry && previousLocale && previousLocale.startsWith("ja")) return previousLocale;
|
|
1468
|
-
return context.hanHint ??
|
|
1785
|
+
return context.hanHint ?? "und-Hani";
|
|
1469
1786
|
}
|
|
1470
1787
|
if (regex.latin.test(char)) {
|
|
1471
1788
|
const hintedLocale = detectLatinLocale(char, context);
|
|
1472
|
-
if (hintedLocale !==
|
|
1473
|
-
if (allowLatinLocaleCarry && previousLocale && isLatinLocale(previousLocale, context) && previousLocale !==
|
|
1789
|
+
if (hintedLocale !== "und-Latn") return hintedLocale;
|
|
1790
|
+
if (allowLatinLocaleCarry && previousLocale && isLatinLocale(previousLocale, context) && previousLocale !== "und-Latn") return previousLocale;
|
|
1474
1791
|
if (context.latinHint) return context.latinHint;
|
|
1475
1792
|
return DEFAULT_LOCALE;
|
|
1476
1793
|
}
|
|
1477
1794
|
return null;
|
|
1478
1795
|
}
|
|
1479
|
-
|
|
1480
1796
|
//#endregion
|
|
1481
1797
|
//#region src/wc/segment.ts
|
|
1482
1798
|
const HARD_BOUNDARY_REGEX = /[\r\n,.!?;:,、。!?;:.。、]/u;
|
|
@@ -1513,7 +1829,7 @@ function segmentTextByLocale(text, options = {}) {
|
|
|
1513
1829
|
continue;
|
|
1514
1830
|
}
|
|
1515
1831
|
if (targetLocale !== currentLocale && detected !== null) {
|
|
1516
|
-
if (currentLocale ===
|
|
1832
|
+
if (currentLocale === "und-Latn" && isLatinLocale(targetLocale, context)) {
|
|
1517
1833
|
const promotionBreakIndex = findLastLatinPromotionBreakIndex(buffer);
|
|
1518
1834
|
if (promotionBreakIndex === -1) {
|
|
1519
1835
|
currentLocale = targetLocale;
|
|
@@ -1580,7 +1896,6 @@ function mergeAdjacentChunks(chunks) {
|
|
|
1580
1896
|
merged.push(last);
|
|
1581
1897
|
return merged;
|
|
1582
1898
|
}
|
|
1583
|
-
|
|
1584
1899
|
//#endregion
|
|
1585
1900
|
//#region src/wc/wc.ts
|
|
1586
1901
|
function wordCounter(text, options = {}) {
|
|
@@ -1695,11 +2010,9 @@ function collectNonWordsAggregate(analyzed, enabled) {
|
|
|
1695
2010
|
}
|
|
1696
2011
|
return collection;
|
|
1697
2012
|
}
|
|
1698
|
-
|
|
1699
2013
|
//#endregion
|
|
1700
2014
|
//#region src/wc/index.ts
|
|
1701
2015
|
var wc_default = wordCounter;
|
|
1702
|
-
|
|
1703
2016
|
//#endregion
|
|
1704
2017
|
//#region src/markdown/section-count.ts
|
|
1705
2018
|
function normalizeText(value) {
|
|
@@ -1764,7 +2077,6 @@ function countSections(input, section, options = {}) {
|
|
|
1764
2077
|
items
|
|
1765
2078
|
};
|
|
1766
2079
|
}
|
|
1767
|
-
|
|
1768
2080
|
//#endregion
|
|
1769
2081
|
//#region src/cli/batch/aggregate.ts
|
|
1770
2082
|
function mergeWordCounterResult(left, right, preserveCollectorSegments) {
|
|
@@ -1978,7 +2290,6 @@ function finalizeBatchSummaryFromFileResults(files, section, wcOptions, options
|
|
|
1978
2290
|
aggregate: section === "all" ? aggregateWordCounterResults(files.map((file) => file.result), preserveCollectorSegments) : aggregateSectionedResults(files.map((file) => file.result), preserveCollectorSegments)
|
|
1979
2291
|
};
|
|
1980
2292
|
}
|
|
1981
|
-
|
|
1982
2293
|
//#endregion
|
|
1983
2294
|
//#region src/cli/batch/jobs/queue.ts
|
|
1984
2295
|
async function runBoundedQueue(total, requestedJobs, worker) {
|
|
@@ -1998,7 +2309,6 @@ async function runBoundedQueue(total, requestedJobs, worker) {
|
|
|
1998
2309
|
await Promise.all(Array.from({ length: concurrency }, () => runWorker()));
|
|
1999
2310
|
return results;
|
|
2000
2311
|
}
|
|
2001
|
-
|
|
2002
2312
|
//#endregion
|
|
2003
2313
|
//#region src/cli/path/load.ts
|
|
2004
2314
|
function isProbablyBinary(buffer) {
|
|
@@ -2015,7 +2325,6 @@ function isProbablyBinary(buffer) {
|
|
|
2015
2325
|
}
|
|
2016
2326
|
return suspicious / sampleSize > .3;
|
|
2017
2327
|
}
|
|
2018
|
-
|
|
2019
2328
|
//#endregion
|
|
2020
2329
|
//#region src/cli/batch/jobs/read-input.ts
|
|
2021
2330
|
async function readBatchInput(path, options) {
|
|
@@ -2046,7 +2355,6 @@ async function readBatchInput(path, options) {
|
|
|
2046
2355
|
content: buffer.toString("utf8")
|
|
2047
2356
|
};
|
|
2048
2357
|
}
|
|
2049
|
-
|
|
2050
2358
|
//#endregion
|
|
2051
2359
|
//#region src/cli/batch/jobs/load-count.ts
|
|
2052
2360
|
async function countBatchInputsWithJobs(filePaths, options) {
|
|
@@ -2101,47 +2409,6 @@ async function countBatchInputsWithJobs(filePaths, options) {
|
|
|
2101
2409
|
skipped
|
|
2102
2410
|
};
|
|
2103
2411
|
}
|
|
2104
|
-
|
|
2105
|
-
//#endregion
|
|
2106
|
-
//#region src/cli/batch/jobs/load-count-worker.ts
|
|
2107
|
-
var WorkerRouteUnavailableError = class extends Error {};
|
|
2108
|
-
function isFallbackFriendlyWorkerError(error) {
|
|
2109
|
-
if (typeof error !== "object" || error === null) return false;
|
|
2110
|
-
const code = "code" in error ? String(error.code) : "";
|
|
2111
|
-
if (code === "ERR_WORKER_PATH" || code === "ERR_WORKER_UNSUPPORTED_EXTENSION" || code === "ERR_UNKNOWN_FILE_EXTENSION" || code === "ERR_MODULE_NOT_FOUND") return true;
|
|
2112
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2113
|
-
return message.includes("Unknown file extension") || message.includes("Cannot find module");
|
|
2114
|
-
}
|
|
2115
|
-
async function countBatchInputsWithWorkerJobs(filePaths, options) {
|
|
2116
|
-
if (process.env.WORD_COUNTER_DISABLE_WORKER_JOBS === "1" || process.env.WORD_COUNTER_DISABLE_EXPERIMENTAL_WORKERS === "1") throw new WorkerRouteUnavailableError("Worker route disabled by environment.");
|
|
2117
|
-
let workerPoolModule;
|
|
2118
|
-
try {
|
|
2119
|
-
workerPoolModule = await import("./worker-pool.mjs");
|
|
2120
|
-
} catch (error) {
|
|
2121
|
-
throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
2122
|
-
}
|
|
2123
|
-
try {
|
|
2124
|
-
return await workerPoolModule.countBatchInputsWithWorkerPool({
|
|
2125
|
-
filePaths,
|
|
2126
|
-
jobs: options.jobs,
|
|
2127
|
-
section: options.section,
|
|
2128
|
-
wcOptions: options.wcOptions,
|
|
2129
|
-
preserveCollectorSegments: options.preserveCollectorSegments,
|
|
2130
|
-
onFileProcessed: options.onFileProcessed
|
|
2131
|
-
});
|
|
2132
|
-
} catch (error) {
|
|
2133
|
-
if (error instanceof workerPoolModule.WorkerPoolTaskFatalError) {
|
|
2134
|
-
if (error.code === "EMFILE" || error.code === "ENFILE") throw createResourceLimitError(error.path, {
|
|
2135
|
-
code: error.code,
|
|
2136
|
-
message: error.message
|
|
2137
|
-
}, options.jobs, resolveBatchJobsLimit());
|
|
2138
|
-
throw new Error(error.message);
|
|
2139
|
-
}
|
|
2140
|
-
if (error instanceof workerPoolModule.WorkerPoolUnavailableError || isFallbackFriendlyWorkerError(error)) throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
2141
|
-
throw error;
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
|
|
2145
2412
|
//#endregion
|
|
2146
2413
|
//#region src/cli/batch/jobs/render.ts
|
|
2147
2414
|
function finalizeBatchJobsSummary(files, section, wcOptions, options = {}) {
|
|
@@ -2150,7 +2417,6 @@ function finalizeBatchJobsSummary(files, section, wcOptions, options = {}) {
|
|
|
2150
2417
|
preserveCollectorSegments: options.preserveCollectorSegments
|
|
2151
2418
|
});
|
|
2152
2419
|
}
|
|
2153
|
-
|
|
2154
2420
|
//#endregion
|
|
2155
2421
|
//#region src/cli/path/resolve.ts
|
|
2156
2422
|
async function expandDirectory(rootPath, directoryPath, recursive, extensionFilter, regexFilter, skipped, recordRegexExcluded, debug, stats) {
|
|
@@ -2353,7 +2619,6 @@ async function resolveBatchFilePaths(pathInputs, options) {
|
|
|
2353
2619
|
skipped
|
|
2354
2620
|
};
|
|
2355
2621
|
}
|
|
2356
|
-
|
|
2357
2622
|
//#endregion
|
|
2358
2623
|
//#region src/cli/progress/reporter.ts
|
|
2359
2624
|
const PROGRESS_BAR_WIDTH = 20;
|
|
@@ -2457,7 +2722,6 @@ function createBatchProgressReporter(options) {
|
|
|
2457
2722
|
}
|
|
2458
2723
|
};
|
|
2459
2724
|
}
|
|
2460
|
-
|
|
2461
2725
|
//#endregion
|
|
2462
2726
|
//#region src/cli/batch/run.ts
|
|
2463
2727
|
async function runBatchCount(options) {
|
|
@@ -2609,19 +2873,16 @@ async function runBatchCount(options) {
|
|
|
2609
2873
|
});
|
|
2610
2874
|
return summary;
|
|
2611
2875
|
}
|
|
2612
|
-
|
|
2613
2876
|
//#endregion
|
|
2614
2877
|
//#region src/cli/batch/jobs/strategy.ts
|
|
2615
2878
|
function resolveBatchJobsStrategy(_jobs) {
|
|
2616
2879
|
return "load-count";
|
|
2617
2880
|
}
|
|
2618
|
-
|
|
2619
2881
|
//#endregion
|
|
2620
2882
|
//#region src/utils/show-singular-or-plural-word.ts
|
|
2621
2883
|
function showSingularOrPluralWord(count, word) {
|
|
2622
2884
|
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
2623
2885
|
}
|
|
2624
|
-
|
|
2625
2886
|
//#endregion
|
|
2626
2887
|
//#region src/cli/output/render.ts
|
|
2627
2888
|
function getCountUnit(mode) {
|
|
@@ -2766,7 +3027,6 @@ function renderPerFileStandard(summary, labels, resolveTotalOfOverride) {
|
|
|
2766
3027
|
}
|
|
2767
3028
|
renderStandardResult(summary.aggregate, labels.overall, resolveTotalOfOverride?.(summary.aggregate));
|
|
2768
3029
|
}
|
|
2769
|
-
|
|
2770
3030
|
//#endregion
|
|
2771
3031
|
//#region src/cli/output/normalize-base.ts
|
|
2772
3032
|
function normalizeWordCounterResultBase(result) {
|
|
@@ -2805,7 +3065,6 @@ function normalizeBatchSummaryBase(summary) {
|
|
|
2805
3065
|
normalizeResultBase(summary.aggregate);
|
|
2806
3066
|
return summary;
|
|
2807
3067
|
}
|
|
2808
|
-
|
|
2809
3068
|
//#endregion
|
|
2810
3069
|
//#region src/cli/runtime/options.ts
|
|
2811
3070
|
function hasPathInput(pathValues) {
|
|
@@ -2932,7 +3191,6 @@ function resolveCountRunOptions(options) {
|
|
|
2932
3191
|
function formatInputReadError(error) {
|
|
2933
3192
|
return `Failed to read input: ${error instanceof Error ? error.message : String(error)}`;
|
|
2934
3193
|
}
|
|
2935
|
-
|
|
2936
3194
|
//#endregion
|
|
2937
3195
|
//#region src/cli/runtime/batch.ts
|
|
2938
3196
|
async function executeBatchCount({ argv, options, runtime, resolved, debug, teeEnabled }) {
|
|
@@ -3070,7 +3328,6 @@ async function executeBatchCount({ argv, options, runtime, resolved, debug, teeE
|
|
|
3070
3328
|
}
|
|
3071
3329
|
renderStandardResult(summary.aggregate, labels.overall, aggregateTotalOfOverride);
|
|
3072
3330
|
}
|
|
3073
|
-
|
|
3074
3331
|
//#endregion
|
|
3075
3332
|
//#region src/cli/runtime/input.ts
|
|
3076
3333
|
async function readStdin() {
|
|
@@ -3088,7 +3345,6 @@ async function resolveInput(textTokens) {
|
|
|
3088
3345
|
if (textTokens.length > 0) return textTokens.join(" ");
|
|
3089
3346
|
return readStdin();
|
|
3090
3347
|
}
|
|
3091
|
-
|
|
3092
3348
|
//#endregion
|
|
3093
3349
|
//#region src/cli/runtime/single.ts
|
|
3094
3350
|
async function executeSingleCount({ textTokens, options, resolved }) {
|
|
@@ -3129,17 +3385,23 @@ async function executeSingleCount({ textTokens, options, resolved }) {
|
|
|
3129
3385
|
}
|
|
3130
3386
|
renderStandardResult(displayResult, labels.overall, totalOfOverride);
|
|
3131
3387
|
}
|
|
3132
|
-
|
|
3133
3388
|
//#endregion
|
|
3134
3389
|
//#region src/command.ts
|
|
3135
3390
|
async function runCli(argv = process.argv, runtime = {}) {
|
|
3391
|
+
if (isExplicitDoctorInvocation(argv)) {
|
|
3392
|
+
await executeDoctorCommand({
|
|
3393
|
+
argv,
|
|
3394
|
+
runtime: runtime.doctor
|
|
3395
|
+
});
|
|
3396
|
+
return;
|
|
3397
|
+
}
|
|
3136
3398
|
const program = new Command();
|
|
3137
3399
|
const parseMode = (value) => {
|
|
3138
3400
|
const normalized = normalizeMode(value);
|
|
3139
3401
|
if (!normalized) throw new Error(`Invalid mode: ${value}`);
|
|
3140
3402
|
return normalized;
|
|
3141
3403
|
};
|
|
3142
|
-
program.name("word-counter").description("Locale-aware word counting powered by Intl.Segmenter.").version(getFormattedVersionLabel(), "-v, --version", "output the version number");
|
|
3404
|
+
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 doctor [options] report runtime diagnostics for this host");
|
|
3143
3405
|
configureProgramOptions(program, parseMode);
|
|
3144
3406
|
program.action(async (textTokens, options) => {
|
|
3145
3407
|
if (options.printJobsLimit) {
|
|
@@ -3226,7 +3488,6 @@ async function runCli(argv = process.argv, runtime = {}) {
|
|
|
3226
3488
|
});
|
|
3227
3489
|
await program.parseAsync(argv);
|
|
3228
3490
|
}
|
|
3229
|
-
|
|
3230
3491
|
//#endregion
|
|
3231
3492
|
//#region src/bin.ts
|
|
3232
3493
|
runCli().catch((error) => {
|
|
@@ -3234,7 +3495,7 @@ runCli().catch((error) => {
|
|
|
3234
3495
|
console.error("Failed to run CLI:", message);
|
|
3235
3496
|
process.exitCode = 1;
|
|
3236
3497
|
});
|
|
3237
|
-
|
|
3238
3498
|
//#endregion
|
|
3239
|
-
export {
|
|
3499
|
+
export {};
|
|
3500
|
+
|
|
3240
3501
|
//# sourceMappingURL=bin.mjs.map
|