cspell 9.3.2 → 9.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,13 @@
1
1
  import { createRequire } from "node:module";
2
- import chalk, { Chalk } from "chalk";
3
2
  import { isAsyncIterable, opFilter, opMap, opTap, operators, pipeAsync, pipeAsync as asyncPipe, toAsyncIterable, toAsyncIterable as mergeAsyncIterables } from "@cspell/cspell-pipe";
4
3
  import * as cspell from "cspell-lib";
5
- import { ENV_CSPELL_GLOB_ROOT, IncludeExcludeFlag, SuggestionError, Text, checkTextDocument, combineTextAndLanguageSettings, createPerfTimer, extractDependencies, extractImportErrors, fileToDocument, getDefaultSettings, getDictionary, getGlobalSettingsAsync, getSystemFeatureFlags, isBinaryFile, isSpellingDictionaryLoadError, mergeSettings, setLogger, shouldCheckDocument, spellCheckDocument, suggestionsForWords, traceWordsAsync } from "cspell-lib";
4
+ import { ENV_CSPELL_GLOB_ROOT, IncludeExcludeFlag, MessageTypes, SuggestionError, Text, checkTextDocument, combineTextAndLanguageSettings, createPerfTimer, extractDependencies, extractImportErrors, fileToDocument, getDefaultSettings, getDictionary, getGlobalSettingsAsync, getSystemFeatureFlags, isBinaryFile, isSpellingDictionaryLoadError, mergeSettings, setLogger, shouldCheckDocument, spellCheckDocument, suggestionsForWords, traceWordsAsync } from "cspell-lib";
6
5
  import assert from "node:assert";
7
- import { format, formatWithOptions } from "node:util";
6
+ import { format, formatWithOptions, stripVTControlCharacters } from "node:util";
8
7
  import { isUrlLike, toFileDirURL, toFilePathOrHref, toFileURL, urlRelative } from "@cspell/url";
8
+ import chalk, { Chalk } from "chalk";
9
9
  import { makeTemplate } from "chalk-template";
10
+ import ansiRegex from "ansi-regex";
10
11
  import fs, { stat } from "node:fs/promises";
11
12
  import { MutableCSpellConfigFile, createReaderWriter, cspellConfigFileSchema, isCfgArrayNode } from "cspell-config-lib";
12
13
  import { promises } from "node:fs";
@@ -14,13 +15,13 @@ import { fileURLToPath } from "node:url";
14
15
  import * as path$1 from "node:path";
15
16
  import path, { isAbsolute, posix, relative, resolve, sep } from "node:path";
16
17
  import { opMap as opMap$1, pipe } from "@cspell/cspell-pipe/sync";
17
- import { IssueType, MessageTypes, unknownWordsChoices } from "@cspell/cspell-types";
18
- import { _debug } from "cspell-dictionary";
18
+ import { IssueType, MessageTypes as MessageTypes$1, unknownWordsChoices } from "@cspell/cspell-types";
19
+ import { dictionaryCacheEnableLogging, dictionaryCacheGetLog } from "cspell-dictionary";
19
20
  import { GitIgnore, findRepoRoot } from "cspell-gitignore";
20
21
  import { GlobMatcher, fileOrGlobToGlob, workaroundPicomatchBug } from "cspell-glob";
21
22
  import crypto from "node:crypto";
22
23
  import streamConsumers from "node:stream/consumers";
23
- import { readFileText, toURL } from "cspell-io";
24
+ import { getStat, readFileText, toURL } from "cspell-io";
24
25
  import { glob } from "tinyglobby";
25
26
  import * as readline from "node:readline";
26
27
  import { parse, stringify } from "flatted";
@@ -88,7 +89,7 @@ var IOError = class extends ApplicationError {
88
89
  return this.cause.code === "ENOENT";
89
90
  }
90
91
  };
91
- function toError(e) {
92
+ function toError$1(e) {
92
93
  if (isError(e)) return e;
93
94
  if (isErrorLike(e)) {
94
95
  const ex = new Error(e.message, { cause: e });
@@ -108,10 +109,349 @@ function isErrorLike(e) {
108
109
  }
109
110
  function toApplicationError(e, message) {
110
111
  if (e instanceof ApplicationError && !message) return e;
111
- const err = toError(e);
112
+ const err = toError$1(e);
112
113
  return new ApplicationError(message ?? err.message, void 0, err);
113
114
  }
114
115
 
116
+ //#endregion
117
+ //#region src/util/perfMeasurements.ts
118
+ function getPerfMeasurements() {
119
+ const measurements = performance.getEntriesByType("measure");
120
+ const root = {
121
+ depth: -1,
122
+ totalTimeMs: 0,
123
+ nestedTimeMs: 0,
124
+ children: /* @__PURE__ */ new Map()
125
+ };
126
+ if (!measurements.length) return [];
127
+ const stack = [];
128
+ let depth = 0;
129
+ for (let i = 0; i < measurements.length; i++) {
130
+ const m = measurements[i];
131
+ rollUpStack(m.startTime);
132
+ const s = {
133
+ m,
134
+ p: addToParent(depth === 0 ? root : stack[depth - 1].p, m)
135
+ };
136
+ stack[depth++] = s;
137
+ }
138
+ sortChildren(root);
139
+ return [...root.children.values()].flatMap((r) => [...flattenChildren(r)]);
140
+ function contains(m, t) {
141
+ const stop = m.startTime + m.duration;
142
+ return t >= m.startTime && t < stop;
143
+ }
144
+ function rollUpStack(t) {
145
+ for (; depth > 0 && !contains(stack[depth - 1].m, t); --depth);
146
+ }
147
+ function addToParent(p, m) {
148
+ p.children ??= /* @__PURE__ */ new Map();
149
+ p.nestedTimeMs += m.duration;
150
+ return updateChild(p.children, m, p.depth + 1);
151
+ }
152
+ function updateChild(children, m, depth$1) {
153
+ const p = children.get(m.name);
154
+ if (p) {
155
+ p.totalTimeMs += m.duration;
156
+ p.count += 1;
157
+ p.minTimeMs = Math.min(p.minTimeMs, m.duration);
158
+ p.maxTimeMs = Math.max(p.maxTimeMs, m.duration);
159
+ return p;
160
+ }
161
+ const n = {
162
+ name: m.name,
163
+ depth: depth$1,
164
+ totalTimeMs: m.duration,
165
+ nestedTimeMs: 0,
166
+ count: 1,
167
+ minTimeMs: m.duration,
168
+ maxTimeMs: m.duration
169
+ };
170
+ children.set(m.name, n);
171
+ return n;
172
+ }
173
+ function* flattenChildren(m) {
174
+ yield m;
175
+ if (!m.children) return;
176
+ for (const child of m.children.values()) yield* flattenChildren(child);
177
+ }
178
+ function sortChildren(m) {
179
+ if (!m.children) return;
180
+ m.children = new Map([...m.children.entries()].sort((a, b) => b[1].totalTimeMs - a[1].totalTimeMs));
181
+ m.children.forEach(sortChildren);
182
+ }
183
+ }
184
+
185
+ //#endregion
186
+ //#region src/util/ansi.ts
187
+ function isAnsiString(s) {
188
+ return s.includes("\x1B") || s.includes("›");
189
+ }
190
+ /**
191
+ *
192
+ * @param s - the string to measure - should NOT contains ANSI codes
193
+ * @param tabWidth -
194
+ * @returns
195
+ */
196
+ function width(s, tabWidth = 1) {
197
+ return s.replaceAll("…", ".").replaceAll(" ", " ".repeat(tabWidth)).replaceAll(/\p{M}/gu, "").replaceAll(/\p{L}/gu, ".").replaceAll(/[\u0000-\u001F\u0300-\u036F]/g, "").replaceAll(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, ".").length;
198
+ }
199
+ /**
200
+ * Measure the width of a string containing ANSI control characters.
201
+ * @param s - string to measure with width in characters.
202
+ * @returns the approximate number of screen characters.
203
+ */
204
+ function ansiWidth(s) {
205
+ return width(stripVTControlCharacters(s));
206
+ }
207
+ function fragmentString(str, splitOnRegex, sType) {
208
+ const fragments = [];
209
+ let lastIndex = 0;
210
+ for (const match of str.matchAll(new RegExp(splitOnRegex))) {
211
+ if (match.index > lastIndex) fragments.push({
212
+ type: "text",
213
+ text: str.slice(lastIndex, match.index)
214
+ });
215
+ fragments.push({
216
+ type: sType,
217
+ text: match[0]
218
+ });
219
+ lastIndex = match.index + match[0].length;
220
+ }
221
+ if (lastIndex < str.length) fragments.push({
222
+ type: "text",
223
+ text: str.slice(lastIndex)
224
+ });
225
+ return fragments;
226
+ }
227
+ const ansi = ansiRegex();
228
+ function parseAnsiStr(str) {
229
+ return fragmentString(str, ansi, "ansi");
230
+ }
231
+ /**
232
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
233
+ * @param str - the text to prune - ANSI is supported
234
+ * @param maxWidth - the maximum width of the text
235
+ * @param pad - the string to use for padding, default is '…'
236
+ * @returns the pruned text
237
+ */
238
+ function pruneAnsiTextEnd(str, maxWidth, pad$1 = "…") {
239
+ if (!maxWidth || maxWidth <= 0) return str;
240
+ if (str.length <= maxWidth) return str;
241
+ if (ansiWidth(str) <= maxWidth) return str;
242
+ const padWidth$1 = ansiWidth(pad$1);
243
+ const fragments = parseAnsiStr(str);
244
+ let remaining = maxWidth - padWidth$1;
245
+ for (const frag of fragments) {
246
+ if (frag.type !== "text") continue;
247
+ if (remaining <= 0) {
248
+ frag.text = "";
249
+ continue;
250
+ }
251
+ const pruned = pruneTextEnd(frag.text, remaining, pad$1);
252
+ if (pruned !== frag.text) {
253
+ frag.text = pruned;
254
+ remaining = 0;
255
+ continue;
256
+ }
257
+ remaining -= width(frag.text);
258
+ }
259
+ return fragments.map((frag) => frag.text).join("");
260
+ }
261
+ /**
262
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
263
+ * @param str - the text to prune - ANSI is supported
264
+ * @param maxWidth - the maximum width of the text
265
+ * @param pad - the string to use for padding, default is '…'
266
+ * @returns the pruned text
267
+ */
268
+ function pruneAnsiTextStart(str, maxWidth, pad$1 = "…") {
269
+ if (!maxWidth || maxWidth <= 0) return str;
270
+ if (str.length <= maxWidth) return str;
271
+ if (ansiWidth(str) <= maxWidth) return str;
272
+ const padWidth$1 = ansiWidth(pad$1);
273
+ const fragments = parseAnsiStr(str);
274
+ let remaining = maxWidth - padWidth$1;
275
+ for (const frag of fragments.reverse()) {
276
+ if (frag.type !== "text") continue;
277
+ if (remaining <= 0) {
278
+ frag.text = "";
279
+ continue;
280
+ }
281
+ const pruned = pruneTextStart(frag.text, remaining, pad$1);
282
+ if (pruned !== frag.text) {
283
+ frag.text = pruned;
284
+ remaining = 0;
285
+ continue;
286
+ }
287
+ remaining -= width(frag.text);
288
+ }
289
+ return fragments.reverse().map((frag) => frag.text).join("");
290
+ }
291
+ /**
292
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
293
+ * @param str - the text to prune - ANSI is not supported
294
+ * @param maxWidth - the maximum width of the text
295
+ * @param pad - the string to use for padding, default is '…'
296
+ * @returns the pruned text
297
+ */
298
+ function pruneTextEnd(str, maxWidth, pad$1 = "…") {
299
+ if (!maxWidth || maxWidth <= 0) return str;
300
+ if (str.length <= maxWidth) return str;
301
+ if (isAnsiString(str)) return pruneAnsiTextEnd(str, maxWidth, pad$1);
302
+ const maxWidthWithPad = maxWidth - width(pad$1);
303
+ const letters = [...str];
304
+ let len = 0;
305
+ for (let i = 0; i < letters.length; i++) {
306
+ const c = letters[i];
307
+ len += width(c);
308
+ if (len > maxWidthWithPad) {
309
+ let j = i + 1;
310
+ while (j < letters.length && width(letters[j]) === 0) ++j;
311
+ return j === letters.length ? str : letters.slice(0, i).join("") + pad$1;
312
+ }
313
+ }
314
+ return str;
315
+ }
316
+ /**
317
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
318
+ * @param str - the text to prune - ANSI is not supported
319
+ * @param maxWidth - the maximum width of the text
320
+ * @param pad - the string to use for padding, default is '…'
321
+ * @returns the pruned text
322
+ */
323
+ function pruneTextStart(str, maxWidth, pad$1 = "…") {
324
+ if (!maxWidth || maxWidth <= 0) return str;
325
+ if (str.length <= maxWidth) return str;
326
+ const maxWidthWithPad = maxWidth - width(pad$1);
327
+ const letters = [...str];
328
+ let len = 0;
329
+ for (let i = letters.length - 1; i >= 1; i--) {
330
+ const c = letters[i];
331
+ len += width(c);
332
+ if (len > maxWidthWithPad) {
333
+ i += 1;
334
+ while (i < letters.length && width(letters[i]) === 0) ++i;
335
+ return pad$1 + letters.slice(i).join("");
336
+ }
337
+ }
338
+ return str;
339
+ }
340
+
341
+ //#endregion
342
+ //#region src/util/pad.ts
343
+ function pad(s, w) {
344
+ const p = padWidth(s, w);
345
+ if (!p) return s;
346
+ return s.padEnd(p + s.length);
347
+ }
348
+ function padWidth(s, target) {
349
+ const sWidth = ansiWidth(s);
350
+ return Math.max(target - sWidth, 0);
351
+ }
352
+ function padLeft(s, w) {
353
+ const p = padWidth(s, w);
354
+ if (!p) return s;
355
+ return s.padStart(p + s.length);
356
+ }
357
+
358
+ //#endregion
359
+ //#region src/util/table.ts
360
+ function tableToLines(table, deliminator) {
361
+ const del = deliminator || table.deliminator || " | ";
362
+ const columnWidths = [];
363
+ const columnAlignments = table.columnAlignments || [];
364
+ const maxColumnWidthsMap = table.maxColumnWidths || {};
365
+ const tableIndent = table.indent ? typeof table.indent === "number" ? " ".repeat(table.indent) : table.indent : "";
366
+ const { header, rows } = table;
367
+ const simpleHeader = header.map((col) => Array.isArray(col) ? col[1] : col);
368
+ const columnFieldNames = header.map((col) => Array.isArray(col) ? col[0] : col);
369
+ const maxColumnWidths = columnFieldNames.map((field, idx) => maxColumnWidthsMap[field] ?? maxColumnWidthsMap[idx]);
370
+ function getCell(row, col) {
371
+ return getCellFromRow(rows[row], col);
372
+ }
373
+ function getCellFromRow(row, col) {
374
+ if (!row) return void 0;
375
+ if (Array.isArray(row)) return row[col];
376
+ return row[columnFieldNames[col]];
377
+ }
378
+ function rowToCells(row) {
379
+ if (Array.isArray(row)) return row;
380
+ return columnFieldNames.map((fieldName) => row[fieldName]);
381
+ }
382
+ function getText(col, maxWidth) {
383
+ return !col ? "" : typeof col === "string" ? pruneTextEnd(col, maxWidth) : col(maxWidth);
384
+ }
385
+ function getRCText(row, col, maxWidth) {
386
+ return getText(getCell(row, col), maxWidth);
387
+ }
388
+ function recordHeaderWidths(header$1) {
389
+ header$1.forEach((col, idx) => {
390
+ columnWidths[idx] = Math.max(ansiWidth(col), columnWidths[idx] || 0);
391
+ });
392
+ }
393
+ function recordColWidths() {
394
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) for (let colIndex = 0; colIndex < columnFieldNames.length; colIndex++) columnWidths[colIndex] = Math.max(ansiWidth(getRCText(rowIndex, colIndex, void 0)), columnWidths[colIndex] || 0);
395
+ }
396
+ function justifyRow(c, i) {
397
+ return columnAlignments[i] === "R" ? padLeft(c, columnWidths[i]) : pad(c, columnWidths[i]);
398
+ }
399
+ function toHeaderLine(header$1) {
400
+ return tableIndent + decorateRowWith(header$1.map((c, i) => getText(c, columnWidths[i])), justifyRow, headerDecorator).join(del);
401
+ }
402
+ function toLine(row) {
403
+ return tableIndent + decorateRowWith(rowToCells(row).map((c, i) => getText(c, columnWidths[i])), justifyRow).join(del);
404
+ }
405
+ function* process$1() {
406
+ if (table.title) yield table.title;
407
+ yield toHeaderLine(simpleHeader);
408
+ yield* rows.map(toLine);
409
+ }
410
+ function sumColumnWidths() {
411
+ return columnWidths.reduce((sum, width$1) => sum + width$1, 0);
412
+ }
413
+ function adjustColWidths() {
414
+ for (let i = 0; i < columnWidths.length; i++) {
415
+ const mw = maxColumnWidths[i];
416
+ if (!mw) continue;
417
+ columnWidths[i] = Math.min(columnWidths[i], mw);
418
+ }
419
+ if (!table.terminalWidth) return;
420
+ const dWidth = (columnWidths.length - 1) * ansiWidth(del);
421
+ const lineWidth = table.terminalWidth - dWidth;
422
+ if (lineWidth <= columnWidths.length * 2) {
423
+ const fixedWidth = Math.max(Math.min(...columnWidths), 5);
424
+ for (let i = 0; i < columnWidths.length; i++) columnWidths[i] = fixedWidth;
425
+ return;
426
+ }
427
+ if (columnWidths.length === 1) {
428
+ columnWidths[0] = lineWidth;
429
+ return;
430
+ }
431
+ function trimWidestColumn(neededToTrim) {
432
+ let first = 0;
433
+ let second = 0;
434
+ for (let i = 0; i < columnWidths.length; i++) if (columnWidths[i] > columnWidths[first]) {
435
+ second = first;
436
+ first = i;
437
+ } else if (columnWidths[i] > columnWidths[second]) second = i;
438
+ const diff = Math.max(columnWidths[first] - columnWidths[second], 1);
439
+ columnWidths[first] -= Math.min(diff, neededToTrim);
440
+ }
441
+ for (let sum = sumColumnWidths(); sum > lineWidth; sum = sumColumnWidths()) trimWidestColumn(sum - lineWidth);
442
+ }
443
+ recordHeaderWidths(simpleHeader);
444
+ recordColWidths();
445
+ adjustColWidths();
446
+ return [...process$1()];
447
+ }
448
+ function headerDecorator(t) {
449
+ return chalk.bold(chalk.underline(t));
450
+ }
451
+ function decorateRowWith(row, ...decorators) {
452
+ return decorators.reduce((row$1, decorator) => row$1.map(decorator), row);
453
+ }
454
+
115
455
  //#endregion
116
456
  //#region src/util/util.ts
117
457
  const uniqueFn = uniqueFilterFnGenerator;
@@ -199,9 +539,10 @@ function reportProgressFileComplete(io, p, cwd, options) {
199
539
  const { idx, filename } = determineFilename(io, p, cwd);
200
540
  const { verbose, debug } = options;
201
541
  const time = reportTime(io, p.elapsedTimeMs, !!p.cached);
202
- const skipped = p.processed === false ? " skipped" : "";
542
+ const skippedReason = p.skippedReason ? ` (${p.skippedReason})` : "";
543
+ const skipped = p.processed === false ? ` skipped${skippedReason}` : "";
203
544
  const hasErrors = p.numErrors ? io.chalk.red` X` : "";
204
- const msg = `${idx} ${filename} ${time}${skipped}${hasErrors}${(skipped && (verbose || debug) || hasErrors || isSlow(p.elapsedTimeMs) || io.getColorLevel() < 1 ? "\n" : "") || "\r"}`;
545
+ const msg = `${idx} ${filename} ${time}${skipped}${hasErrors}${(verbose || debug || hasErrors || isSlow(p.elapsedTimeMs) || io.getColorLevel() < 1 ? "\n" : "") || "\r"}`;
205
546
  io.write(msg);
206
547
  }
207
548
  function reportTime(io, elapsedTimeMs, cached) {
@@ -275,8 +616,9 @@ function getReporter(options, config) {
275
616
  }
276
617
  const resultEmitter = (result) => {
277
618
  if (!fileGlobs.length && !result.files) return;
278
- const { files, issues: issues$1, cachedFiles, filesWithIssues, errors } = result;
619
+ const { files, issues: issues$1, cachedFiles, filesWithIssues, errors, skippedFiles } = result;
279
620
  const numFilesWithIssues = filesWithIssues.size;
621
+ const chalk$1 = stderr.chalk;
280
622
  if (stderr.getColorLevel() > 0) {
281
623
  stderr.write("\r");
282
624
  stderr.clearLine(0);
@@ -286,9 +628,11 @@ function getReporter(options, config) {
286
628
  consoleError("Issues found:");
287
629
  issuesCollection.forEach((issue) => consoleError(issue));
288
630
  }
631
+ const filesChecked = files - (skippedFiles || 0);
289
632
  const cachedFilesText = cachedFiles ? ` (${cachedFiles} from cache)` : "";
633
+ const skippedFilesText = skippedFiles ? `, skipped: ${skippedFiles}` : "";
290
634
  const withErrorsText = errors ? ` with ${errors} error${errors === 1 ? "" : "s"}` : "";
291
- consoleError(`CSpell\u003A Files checked: ${files}${cachedFilesText}, Issues found: ${issues$1} in ${numFilesWithIssues === 1 ? "1 file" : `${numFilesWithIssues} files`}${withErrorsText}.`);
635
+ consoleError(`CSpell\u003A Files checked: ${filesChecked}${cachedFilesText}${skippedFilesText}, Issues found: ${issues$1} in ${numFilesWithIssues === 1 ? "1 file" : `${numFilesWithIssues} files`}${withErrorsText}.`);
292
636
  if (errorCollection?.length && issues$1 > 5) {
293
637
  consoleError("-------------------------------------------");
294
638
  consoleError("Errors:");
@@ -301,14 +645,80 @@ function getReporter(options, config) {
301
645
  consoleError(` Files Skipped : ${perfStats.filesSkipped.toString().padStart(6)}`);
302
646
  consoleError(` Files Cached : ${perfStats.filesCached.toString().padStart(6)}`);
303
647
  consoleError(` Processing Time: ${perfStats.elapsedTimeMs.toFixed(2).padStart(9)}ms`);
304
- consoleError("Stats:");
305
- const stats = Object.entries(perfStats.perf).filter((p) => !!p[1]).map(([key, value]) => [key, value.toFixed(2)]);
306
- const padName = Math.max(...stats.map((s) => s[0].length));
307
- const padValue = Math.max(...stats.map((s) => s[1].length));
308
- stats.sort((a, b) => a[0].localeCompare(b[0]));
309
- for (const [key, value] of stats) value && consoleError(` ${key.padEnd(padName)}: ${value.padStart(padValue)}ms`);
648
+ const tableStats = {
649
+ title: chalk$1.bold("Perf Stats:"),
650
+ header: ["Name", "Time (ms)"],
651
+ columnAlignments: ["L", "R"],
652
+ indent: 2,
653
+ rows: Object.entries(perfStats.perf).filter((p) => !!p[1]).map(([key, value]) => [key, value.toFixed(2)])
654
+ };
655
+ consoleError("");
656
+ for (const line of tableToLines(tableStats)) consoleError(line);
657
+ if (options.verboseLevel) verbosePerfReport();
310
658
  }
311
659
  };
660
+ function verbosePerfReport() {
661
+ const perfMeasurements = getPerfMeasurements();
662
+ if (!perfMeasurements.length) return;
663
+ const notable = extractNotableBySelfTimeInGroup(perfMeasurements);
664
+ const chalk$1 = stderr.chalk;
665
+ const maxDepth = Math.max(...perfMeasurements.map((m) => m.depth));
666
+ const depthIndicator = (d) => "⋅".repeat(d) + " ".repeat(maxDepth - d);
667
+ const rows = perfMeasurements.map((m) => {
668
+ const cbd = (text) => colorByDepth(chalk$1, m.depth, text);
669
+ const cNotable = (text) => notable.has(m) ? chalk$1.yellow(text) : text;
670
+ return [
671
+ chalk$1.dim("⋅".repeat(m.depth)) + colorByDepthGrayscale(stderr.chalk, m.depth, m.name),
672
+ cbd(m.totalTimeMs.toFixed(2) + chalk$1.dim(depthIndicator(m.depth))),
673
+ cbd(cNotable((m.totalTimeMs - m.nestedTimeMs).toFixed(2))),
674
+ cbd(m.count.toString()),
675
+ cbd(m.minTimeMs.toFixed(2)),
676
+ cbd(m.maxTimeMs.toFixed(2)),
677
+ cbd((m.totalTimeMs / m.count).toFixed(2))
678
+ ];
679
+ });
680
+ const table = tableToLines({
681
+ title: chalk$1.bold("Detailed Measurements:"),
682
+ header: [
683
+ "Name",
684
+ "Total Time (ms)",
685
+ "Self (ms)",
686
+ "Count",
687
+ "Min (ms)",
688
+ "Max (ms)",
689
+ "Avg (ms)"
690
+ ],
691
+ rows,
692
+ columnAlignments: [
693
+ "L",
694
+ "R",
695
+ "R",
696
+ "R",
697
+ "R",
698
+ "R",
699
+ "R"
700
+ ],
701
+ indent: 2
702
+ });
703
+ consoleError("\n-------------------------------------------\n");
704
+ for (const line of table) consoleError(line);
705
+ }
706
+ function colorByDepth(chalk$1, depth, text) {
707
+ const colors = [
708
+ chalk$1.green,
709
+ chalk$1.cyan,
710
+ chalk$1.blue,
711
+ chalk$1.magenta,
712
+ chalk$1.red
713
+ ];
714
+ const color = colors[depth % colors.length];
715
+ if (depth / colors.length >= 1) return chalk$1.dim(color(text));
716
+ return color(text);
717
+ }
718
+ function colorByDepthGrayscale(chalk$1, depth, text) {
719
+ const grayLevel = Math.max(32, 255 - depth * 20);
720
+ return chalk$1.rgb(grayLevel, grayLevel, grayLevel)(text);
721
+ }
312
722
  function collectPerfStats(p) {
313
723
  if (p.cached) {
314
724
  perfStats.filesCached++;
@@ -334,6 +744,27 @@ function getReporter(options, config) {
334
744
  features: void 0
335
745
  };
336
746
  }
747
+ function extractNotableBySelfTimeInGroup(measurements) {
748
+ const notable = /* @__PURE__ */ new Set();
749
+ if (!measurements.length) return notable;
750
+ let highest;
751
+ let highestSelfTime = 0;
752
+ for (const m of measurements) {
753
+ if (m.depth === 0 || !highest) {
754
+ if (highest) notable.add(highest);
755
+ highest = m;
756
+ highestSelfTime = m.totalTimeMs - m.nestedTimeMs;
757
+ continue;
758
+ }
759
+ const selfTime = m.totalTimeMs - m.nestedTimeMs;
760
+ if (selfTime > highestSelfTime) {
761
+ highest = m;
762
+ highestSelfTime = selfTime;
763
+ }
764
+ }
765
+ if (highest) notable.add(highest);
766
+ return notable;
767
+ }
337
768
  function formatIssue(io, templateStr, issue, maxIssueTextWidth) {
338
769
  function clean$1(t$1) {
339
770
  return t$1.replace(/\s+/, " ");
@@ -443,7 +874,7 @@ async function fileExists(url) {
443
874
  try {
444
875
  return (await promises.stat(url)).isFile();
445
876
  } catch (e) {
446
- if (toError(e).code === "ENOENT") return false;
877
+ if (toError$1(e).code === "ENOENT") return false;
447
878
  throw e;
448
879
  }
449
880
  }
@@ -766,7 +1197,7 @@ const pkgDir = _dirname;
766
1197
  //#endregion
767
1198
  //#region src/pkgInfo.ts
768
1199
  const name = "cspell";
769
- const version$1 = "9.3.2";
1200
+ const version$1 = "9.6.0";
770
1201
  const engines = { node: ">=20" };
771
1202
  const npmPackage = {
772
1203
  name,
@@ -987,7 +1418,7 @@ function readFileInfo(filename, encoding = UTF8, handleNotFound = false) {
987
1418
  text,
988
1419
  filename
989
1420
  }), (e) => {
990
- const error = toError(e);
1421
+ const error = toError$1(e);
991
1422
  return handleNotFound && error.code === "EISDIR" ? Promise.resolve({
992
1423
  text: "",
993
1424
  filename,
@@ -999,6 +1430,11 @@ function readFileInfo(filename, encoding = UTF8, handleNotFound = false) {
999
1430
  }) : Promise.reject(new IOError(`Error reading file: "${filename}"`, error));
1000
1431
  });
1001
1432
  }
1433
+ async function getFileSize(filename) {
1434
+ const s = await getStat(filename);
1435
+ if (!(s instanceof Error)) return s.size;
1436
+ throw s;
1437
+ }
1002
1438
  function readFile(filename, encoding = UTF8) {
1003
1439
  return readFileInfo(filename, encoding).then((info) => info.text);
1004
1440
  }
@@ -1172,7 +1608,7 @@ var ImplFileEntryCache = class {
1172
1608
  return {
1173
1609
  key: file,
1174
1610
  notFound: true,
1175
- err: toError$1(error)
1611
+ err: toError(error)
1176
1612
  };
1177
1613
  }
1178
1614
  if (this.useChecksum) return this.#getFileDescriptorUsingChecksum(file);
@@ -1287,7 +1723,7 @@ var ImplFileEntryCache = class {
1287
1723
  function isNodeError(error) {
1288
1724
  return typeof error === "object" && error !== null && "code" in error;
1289
1725
  }
1290
- function toError$1(error) {
1726
+ function toError(error) {
1291
1727
  if (error instanceof Error) return error;
1292
1728
  if (typeof error === "string") return new Error(error);
1293
1729
  return new Error("Unknown error", { cause: error });
@@ -1376,7 +1812,7 @@ var DiskCache = class {
1376
1812
  cached
1377
1813
  };
1378
1814
  }
1379
- async setCachedLintResults({ fileInfo, elapsedTimeMs: _, cached: __,...result }, dependsUponFiles) {
1815
+ async setCachedLintResults({ fileInfo, elapsedTimeMs: _, cached: __, ...result }, dependsUponFiles) {
1380
1816
  const fileDescriptor = await this.fileEntryCache.getFileDescriptor(fileInfo.filename);
1381
1817
  const meta = fileDescriptor.meta;
1382
1818
  if (fileDescriptor.notFound || !meta) return;
@@ -1397,7 +1833,7 @@ var DiskCache = class {
1397
1833
  this.ocCacheFileResult = new ShallowObjectCollection();
1398
1834
  }
1399
1835
  normalizeResult(result) {
1400
- const { issues, processed, errors, configErrors, reportIssueOptions,...rest } = result;
1836
+ const { issues, processed, errors, configErrors, reportIssueOptions, ...rest } = result;
1401
1837
  if (!Object.keys(rest).length) return this.ocCacheFileResult.get(result);
1402
1838
  return this.ocCacheFileResult.get({
1403
1839
  issues,
@@ -1638,61 +2074,6 @@ async function readConfigHandleError(filename) {
1638
2074
  }
1639
2075
  }
1640
2076
 
1641
- //#endregion
1642
- //#region src/util/extractContext.ts
1643
- function prefCharIndex(text, offset, count = 1) {
1644
- if (offset - count < 0) return 0;
1645
- for (; count > 0 && offset > 0; count--) {
1646
- let code = text.charCodeAt(--offset) || 0;
1647
- if (code === 65039) code = text.charCodeAt(--offset) || 0;
1648
- offset -= (code & 64512) === 56320 ? 1 : 0;
1649
- }
1650
- return offset < 0 ? 0 : offset;
1651
- }
1652
- function nextCharIndex(text, offset, count = 1) {
1653
- if (offset + count >= text.length) return text.length;
1654
- for (; count > 0 && offset < text.length; count--) {
1655
- const code = text.charCodeAt(offset++) || 0;
1656
- offset += (code & 64512) === 55296 ? 1 : 0;
1657
- if (text.charCodeAt(offset) === 65039) offset++;
1658
- }
1659
- return offset > text.length ? text.length : offset;
1660
- }
1661
- function lineContext(lineText, start, end, contextRange) {
1662
- let left = prefCharIndex(lineText, start, contextRange);
1663
- let right = nextCharIndex(lineText, end, contextRange);
1664
- const isLetter = /^\p{L}$/u;
1665
- const isMark = /^\p{M}$/u;
1666
- for (let n = contextRange / 2; n > 0 && left > 0; n--, left--) {
1667
- const c = lineText[left - 1];
1668
- if (isMark.test(c)) {
1669
- if (!isLetter.test(lineText[left - 2])) break;
1670
- left--;
1671
- continue;
1672
- }
1673
- if (!isLetter.test(lineText[left - 1])) break;
1674
- }
1675
- for (let n = contextRange / 2; n > 0 && right < lineText.length; n--, right++) {
1676
- if (!isLetter.test(lineText[right])) break;
1677
- if (isMark.test(lineText[right + 1])) right++;
1678
- }
1679
- left = left < 0 ? 0 : left;
1680
- const t0 = lineText.slice(left, right);
1681
- const tLeft = t0.trimStart();
1682
- left = Math.min(left + t0.length - tLeft.length, start);
1683
- return {
1684
- text: tLeft.trimEnd(),
1685
- offset: left
1686
- };
1687
- }
1688
- function extractContext(tdo, contextRange) {
1689
- const { line, offset, text } = tdo;
1690
- const start = offset - line.offset;
1691
- const context = lineContext(line.text, start, start + text.length, contextRange);
1692
- context.offset += line.offset;
1693
- return context;
1694
- }
1695
-
1696
2077
  //#endregion
1697
2078
  //#region src/util/prefetch.ts
1698
2079
  function* prefetchIterable(iterable, size) {
@@ -1741,7 +2122,7 @@ async function loadReporters(reporters, defaultReporter, config) {
1741
2122
  const { getReporter: getReporter$1 } = await dynamicImport(moduleName, [process.cwd(), pkgDir]);
1742
2123
  return getReporter$1(settings, config);
1743
2124
  } catch (e) {
1744
- throw new ApplicationError(`Failed to load reporter ${moduleName}: ${toError(e).message}`);
2125
+ throw new ApplicationError(`Failed to load reporter ${moduleName}: ${toError$1(e).message}`);
1745
2126
  }
1746
2127
  }
1747
2128
  reporters = !reporters || !reporters.length ? ["default"] : [...reporters];
@@ -1852,6 +2233,7 @@ var LintReporter = class {
1852
2233
  filename,
1853
2234
  elapsedTimeMs: result.elapsedTimeMs,
1854
2235
  processed: result.processed,
2236
+ skippedReason: result.skippedReason,
1855
2237
  numErrors: numIssues || result.errors,
1856
2238
  cached: result.cached,
1857
2239
  perf: result.perf,
@@ -1872,6 +2254,144 @@ function getTimeMeasurer() {
1872
2254
  return () => timer.elapsed;
1873
2255
  }
1874
2256
 
2257
+ //#endregion
2258
+ //#region src/util/unindent.ts
2259
+ /**
2260
+ * Indent each line of a multi-line string.
2261
+ * @param str - multi-line string to left pad
2262
+ * @param padding - the padding to use for all lines except the first.
2263
+ * @param firstLinePadding - optional padding of first line.
2264
+ * @returns
2265
+ */
2266
+ function indent(str, padding, firstLinePadding = "") {
2267
+ let pad$1 = firstLinePadding;
2268
+ const lines = [];
2269
+ for (const line of str.split("\n")) {
2270
+ lines.push(pad$1 + line);
2271
+ pad$1 = padding;
2272
+ }
2273
+ return lines.join("\n");
2274
+ }
2275
+ /**
2276
+ * Inject values into a template string while keeping indentation of multi-line values.
2277
+ * @param template - template string
2278
+ * @param values- values to inject
2279
+ * @returns the injected string
2280
+ */
2281
+ function keepIndent(template, ...values) {
2282
+ const strings = template;
2283
+ const adjValues = [];
2284
+ for (let i = 0; i < values.length; ++i) {
2285
+ const prevLines = strings[i].split("\n");
2286
+ const currLine = prevLines[prevLines.length - 1];
2287
+ const padLen = padLength(currLine);
2288
+ const padding = " ".repeat(padLen);
2289
+ adjValues.push(indent(`${values[i]}`, padding));
2290
+ }
2291
+ return String.raw({ raw: strings }, ...adjValues);
2292
+ }
2293
+ /**
2294
+ * Calculate the padding at the start of the string.
2295
+ * @param s - string to evaluate
2296
+ * @returns number of padding characters
2297
+ */
2298
+ function padLength(s) {
2299
+ return s.length - s.trimStart().length;
2300
+ }
2301
+ function unindent(templateOrString, ...values) {
2302
+ return unindentString(typeof templateOrString === "string" ? templateOrString : keepIndent(templateOrString, ...values));
2303
+ }
2304
+ function unindentString(str) {
2305
+ const lines = str.split("\n");
2306
+ let curPad = str.length;
2307
+ for (const line of lines) {
2308
+ if (!line.trim()) continue;
2309
+ curPad = Math.min(curPad, padLength(line));
2310
+ }
2311
+ return lines.map((line) => line.slice(curPad)).join("\n");
2312
+ }
2313
+
2314
+ //#endregion
2315
+ //#region src/util/unitNumbers.ts
2316
+ const regexUnitNumber = /^((?:\d+(?:\.\d*)?)|(?:\.\d+))([a-z]*)$/i;
2317
+ const unitSizes = {
2318
+ "": 1,
2319
+ b: 1,
2320
+ k: 1024,
2321
+ kb: 1024,
2322
+ m: 1 << 20,
2323
+ mb: 1 << 20,
2324
+ g: 1 << 30,
2325
+ gb: 1 << 30
2326
+ };
2327
+ function parseUnitSize(size) {
2328
+ const match = size.match(regexUnitNumber);
2329
+ const digits = match?.[1] || "";
2330
+ const units = (match?.[2] || "").toLowerCase();
2331
+ if (!match) return {
2332
+ size,
2333
+ digits,
2334
+ units,
2335
+ error: "Invalid size."
2336
+ };
2337
+ if (!units || units in unitSizes) return {
2338
+ size,
2339
+ digits,
2340
+ units
2341
+ };
2342
+ return {
2343
+ size,
2344
+ digits,
2345
+ units,
2346
+ error: `Unknown units. Valid units are: ${Object.keys(unitSizes).filter(Boolean).join(", ").toUpperCase()}.`
2347
+ };
2348
+ }
2349
+ function validateUnitSize(size) {
2350
+ return parseUnitSize(size).error;
2351
+ }
2352
+ function sizeToNumber(size) {
2353
+ const p = parseUnitSize(size);
2354
+ if (p.error) return NaN;
2355
+ return Number.parseFloat(p.digits) * (unitSizes[p.units] || 1);
2356
+ }
2357
+
2358
+ //#endregion
2359
+ //#region src/util/wrap.ts
2360
+ const wrapSep = /\s+|(?<=,)|\.(?=\w)/g;
2361
+ function wordWrapAnsiText(str, maxWidth, indent$1 = "", sep$1 = wrapSep) {
2362
+ if (!maxWidth || maxWidth <= 0) return str;
2363
+ if (str.length <= maxWidth) return str;
2364
+ if (str.includes("\n")) return str.split("\n").map((line$1) => wordWrapAnsiText(line$1, maxWidth, indent$1)).join("\n");
2365
+ const fragments = fragmentString(str, sep$1, "sep");
2366
+ const lines = [];
2367
+ let line = "";
2368
+ for (const text of joinFragments(fragments)) {
2369
+ const lineWidth = ansiWidth(line);
2370
+ const textWidth = ansiWidth(text);
2371
+ if (line && lineWidth + textWidth > maxWidth) {
2372
+ if (line) lines.push(line);
2373
+ line = indent$1 + text.trimStart();
2374
+ continue;
2375
+ }
2376
+ line += text;
2377
+ }
2378
+ if (line) lines.push(line);
2379
+ return lines.join("\n");
2380
+ }
2381
+ function* joinFragments(fragments) {
2382
+ let last;
2383
+ for (const frag of fragments) {
2384
+ if (frag.type === "sep") {
2385
+ if (last) yield last.text;
2386
+ last = frag;
2387
+ continue;
2388
+ }
2389
+ yield last ? last.text + frag.text : frag.text;
2390
+ last = void 0;
2391
+ }
2392
+ if (last) yield last.text;
2393
+ }
2394
+
1875
2395
  //#endregion
1876
2396
  //#region src/util/writeFile.ts
1877
2397
  async function writeFileOrStream(filename, data) {
@@ -1895,6 +2415,206 @@ function writeStream(stream, data) {
1895
2415
  });
1896
2416
  }
1897
2417
 
2418
+ //#endregion
2419
+ //#region src/util/extractContext.ts
2420
+ function prefCharIndex(text, offset, count = 1) {
2421
+ if (offset - count < 0) return 0;
2422
+ for (; count > 0 && offset > 0; count--) {
2423
+ let code = text.charCodeAt(--offset) || 0;
2424
+ if (code === 65039) code = text.charCodeAt(--offset) || 0;
2425
+ offset -= (code & 64512) === 56320 ? 1 : 0;
2426
+ }
2427
+ return offset < 0 ? 0 : offset;
2428
+ }
2429
+ function nextCharIndex(text, offset, count = 1) {
2430
+ if (offset + count >= text.length) return text.length;
2431
+ for (; count > 0 && offset < text.length; count--) {
2432
+ const code = text.charCodeAt(offset++) || 0;
2433
+ offset += (code & 64512) === 55296 ? 1 : 0;
2434
+ if (text.charCodeAt(offset) === 65039) offset++;
2435
+ }
2436
+ return offset > text.length ? text.length : offset;
2437
+ }
2438
+ function lineContext(lineText, start, end, contextRange) {
2439
+ let left = prefCharIndex(lineText, start, contextRange);
2440
+ let right = nextCharIndex(lineText, end, contextRange);
2441
+ const isLetter = /^\p{L}$/u;
2442
+ const isMark = /^\p{M}$/u;
2443
+ for (let n = contextRange / 2; n > 0 && left > 0; n--, left--) {
2444
+ const c = lineText[left - 1];
2445
+ if (isMark.test(c)) {
2446
+ if (!isLetter.test(lineText[left - 2])) break;
2447
+ left--;
2448
+ continue;
2449
+ }
2450
+ if (!isLetter.test(lineText[left - 1])) break;
2451
+ }
2452
+ for (let n = contextRange / 2; n > 0 && right < lineText.length; n--, right++) {
2453
+ if (!isLetter.test(lineText[right])) break;
2454
+ if (isMark.test(lineText[right + 1])) right++;
2455
+ }
2456
+ left = left < 0 ? 0 : left;
2457
+ const t0 = lineText.slice(left, right);
2458
+ const tLeft = t0.trimStart();
2459
+ left = Math.min(left + t0.length - tLeft.length, start);
2460
+ return {
2461
+ text: tLeft.trimEnd(),
2462
+ offset: left
2463
+ };
2464
+ }
2465
+ function extractContext(tdo, contextRange) {
2466
+ const { line, offset, text } = tdo;
2467
+ const start = offset - line.offset;
2468
+ const context = lineContext(line.text, start, start + text.length, contextRange);
2469
+ context.offset += line.offset;
2470
+ return context;
2471
+ }
2472
+
2473
+ //#endregion
2474
+ //#region src/lint/LinterError.ts
2475
+ var LinterError = class extends Error {
2476
+ constructor(message) {
2477
+ super(message);
2478
+ }
2479
+ toString() {
2480
+ return this.message;
2481
+ }
2482
+ };
2483
+
2484
+ //#endregion
2485
+ //#region src/lint/processFile.ts
2486
+ async function processFile(filename, cache, prefetch, processFileOptions) {
2487
+ if (prefetch?.fileResult) return prefetch.fileResult;
2488
+ const { reporter, cfg, configInfo } = processFileOptions;
2489
+ const getElapsedTimeMs = getTimeMeasurer();
2490
+ const reportIssueOptions = prefetch?.reportIssueOptions;
2491
+ const cachedResult = await cache.getCachedLintResults(filename);
2492
+ if (cachedResult) {
2493
+ reporter.debug(`Filename: ${filename}, using cache`);
2494
+ return {
2495
+ ...cachedResult,
2496
+ elapsedTimeMs: getElapsedTimeMs(),
2497
+ reportIssueOptions: {
2498
+ ...cachedResult.reportIssueOptions,
2499
+ ...reportIssueOptions
2500
+ }
2501
+ };
2502
+ }
2503
+ const result = {
2504
+ fileInfo: { filename },
2505
+ issues: [],
2506
+ processed: false,
2507
+ errors: 0,
2508
+ configErrors: 0,
2509
+ elapsedTimeMs: 0,
2510
+ reportIssueOptions
2511
+ };
2512
+ const fileInfo = prefetch?.fileInfo || await readFileInfo(filename, void 0, true);
2513
+ if (fileInfo.errorCode) {
2514
+ if (fileInfo.errorCode !== "EISDIR" && cfg.options.mustFindFiles) {
2515
+ const err = new LinterError(`File not found: "${filename}"`);
2516
+ reporter.error("Linter:", err);
2517
+ result.errors += 1;
2518
+ }
2519
+ return result;
2520
+ }
2521
+ const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
2522
+ const { text } = fileInfo;
2523
+ result.fileInfo = fileInfo;
2524
+ let spellResult = {};
2525
+ try {
2526
+ const { showSuggestions: generateSuggestions, validateDirectives, skipValidation } = cfg.options;
2527
+ const numSuggestions = configInfo.config.numSuggestions ?? 5;
2528
+ const r = await spellCheckDocument(doc, clean({
2529
+ generateSuggestions,
2530
+ numSuggestions,
2531
+ validateDirectives,
2532
+ skipValidation
2533
+ }), configInfo.config);
2534
+ spellResult = r;
2535
+ result.processed = r.checked;
2536
+ result.perf = r.perf ? { ...r.perf } : void 0;
2537
+ result.issues = Text.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
2538
+ } catch (e) {
2539
+ reporter.error(`Failed to process "${filename}"`, toError$1(e));
2540
+ result.errors += 1;
2541
+ }
2542
+ result.elapsedTimeMs = getElapsedTimeMs();
2543
+ const config = spellResult.settingsUsed ?? {};
2544
+ result.reportIssueOptions = mergeReportIssueOptions(spellResult.settingsUsed || configInfo.config, reportIssueOptions);
2545
+ result.configErrors += await reportConfigurationErrors(config, processFileOptions);
2546
+ reportCheckResult(result, doc, spellResult, config, processFileOptions);
2547
+ const dep = calcDependencies(config);
2548
+ await cache.setCachedLintResults(result, dep.files);
2549
+ return result;
2550
+ function mapIssue({ doc: _, ...tdo }) {
2551
+ const context = cfg.showContext ? extractContext(tdo, cfg.showContext) : void 0;
2552
+ return clean({
2553
+ ...tdo,
2554
+ context
2555
+ });
2556
+ }
2557
+ }
2558
+ function reportCheckResult(result, _doc, spellResult, config, processFileOptions) {
2559
+ const { configInfo, reporter, verboseLevel, useColor, cfg, chalk: chalk$1 } = processFileOptions;
2560
+ const elapsed = result.elapsedTimeMs || 0;
2561
+ const dictionaries = config.dictionaries || [];
2562
+ if (verboseLevel > 1) {
2563
+ const dictsUsed = [...dictionaries].sort().map((name$1) => chalk$1.green(name$1)).join(", ");
2564
+ const msg = unindent`
2565
+ File type: ${config.languageId}, Language: ${config.language}, Issues: ${result.issues.length} ${elapsed.toFixed(2)}ms
2566
+ Config file Used: ${relativeToCwd(spellResult.localConfigFilepath || configInfo.source, cfg.root)}
2567
+ Dictionaries Used:
2568
+ ${wordWrapAnsiText(dictsUsed, 70)}`;
2569
+ reporter.info(indent(msg, " "), MessageTypes.Info);
2570
+ }
2571
+ if (cfg.options.debug) {
2572
+ const { enabled, language, languageId, dictionaries: dictionaries$1 } = config;
2573
+ const msg = unindent`\
2574
+ Debug Config: ${formatWithOptions({
2575
+ depth: 2,
2576
+ colors: useColor
2577
+ }, {
2578
+ languageId,
2579
+ enabled,
2580
+ language,
2581
+ dictionaries: dictionaries$1
2582
+ })}`;
2583
+ reporter.debug(msg);
2584
+ }
2585
+ }
2586
+ function calcDependencies(config) {
2587
+ const { configFiles, dictionaryFiles } = extractDependencies(config);
2588
+ return { files: [...configFiles, ...dictionaryFiles] };
2589
+ }
2590
+ async function reportConfigurationErrors(config, processFileOptions) {
2591
+ const { reporter, configErrors } = processFileOptions;
2592
+ const errors = extractImportErrors(config);
2593
+ let count = 0;
2594
+ errors.forEach((ref) => {
2595
+ const key = ref.error.toString();
2596
+ if (configErrors.has(key)) return;
2597
+ configErrors.add(key);
2598
+ count += 1;
2599
+ reporter.error("Configuration", ref.error);
2600
+ });
2601
+ (await getDictionary(config)).dictionaries.forEach((dict) => {
2602
+ const dictErrors = dict.getErrors?.() || [];
2603
+ const msg = `Dictionary Error with (${dict.name})`;
2604
+ dictErrors.forEach((error) => {
2605
+ const key = msg + error.toString();
2606
+ if (configErrors.has(key)) return;
2607
+ configErrors.add(key);
2608
+ count += 1;
2609
+ reporter.error(msg, error);
2610
+ });
2611
+ });
2612
+ return count;
2613
+ }
2614
+ function countConfigErrors(configInfo, processFileOptions) {
2615
+ return reportConfigurationErrors(configInfo.config, processFileOptions);
2616
+ }
2617
+
1898
2618
  //#endregion
1899
2619
  //#region src/lint/lint.ts
1900
2620
  const version = npmPackage.version;
@@ -1903,9 +2623,11 @@ const { opFilterAsync } = operators;
1903
2623
  async function runLint(cfg) {
1904
2624
  const reporter = new LintReporter(cfg.reporter, cfg.options);
1905
2625
  const configErrors = /* @__PURE__ */ new Set();
2626
+ const verboseLevel = calcVerboseLevel(cfg.options);
2627
+ const useColor = cfg.options.color ?? true;
1906
2628
  const timer = getTimeMeasurer();
1907
2629
  const logDictRequests = truthy(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOGGING"));
1908
- if (logDictRequests) _debug.cacheDictionaryEnableLogging(true);
2630
+ if (logDictRequests) dictionaryCacheEnableLogging(true);
1909
2631
  const lintResult = await run();
1910
2632
  if (logDictRequests) await writeDictionaryLog();
1911
2633
  await reporter.result(lintResult);
@@ -1915,7 +2637,10 @@ async function runLint(cfg) {
1915
2637
  function prefetch(filename, configInfo, cache) {
1916
2638
  if (isBinaryFile$1(filename, cfg.root)) return {
1917
2639
  filename,
1918
- result: Promise.resolve({ skip: true })
2640
+ result: Promise.resolve({
2641
+ skip: true,
2642
+ skipReason: "Binary file."
2643
+ })
1919
2644
  };
1920
2645
  const reportIssueOptions = extractReporterIssueOptions(configInfo.config);
1921
2646
  async function fetch$1() {
@@ -1929,7 +2654,18 @@ async function runLint(cfg) {
1929
2654
  } };
1930
2655
  }
1931
2656
  const uri = filenameToUri(filename, cfg.root).href;
1932
- if (!(await shouldCheckDocument({ uri }, {}, configInfo.config)).shouldCheck) return { skip: true };
2657
+ const checkResult = await shouldCheckDocument({ uri }, {}, configInfo.config);
2658
+ if (!checkResult.shouldCheck) return {
2659
+ skip: true,
2660
+ skipReason: checkResult.reason || "Ignored by configuration."
2661
+ };
2662
+ const maxFileSize = processMaxFileSize(cfg.maxFileSize ?? checkResult.settings.maxFileSize);
2663
+ if (maxFileSize) {
2664
+ if (await getFileSize(filename) > maxFileSize) return {
2665
+ skip: true,
2666
+ skipReason: `File exceeded max file size of ${maxFileSize.toLocaleString()}`
2667
+ };
2668
+ }
1933
2669
  return {
1934
2670
  fileInfo: await readFileInfo(filename, void 0, true),
1935
2671
  reportIssueOptions
@@ -1937,97 +2673,8 @@ async function runLint(cfg) {
1937
2673
  }
1938
2674
  return {
1939
2675
  filename,
1940
- result: fetch$1()
1941
- };
1942
- }
1943
- async function processFile(filename, configInfo, cache, prefetch$1) {
1944
- if (prefetch$1?.fileResult) return prefetch$1.fileResult;
1945
- const getElapsedTimeMs = getTimeMeasurer();
1946
- const reportIssueOptions = prefetch$1?.reportIssueOptions;
1947
- const cachedResult = await cache.getCachedLintResults(filename);
1948
- if (cachedResult) {
1949
- reporter.debug(`Filename: ${filename}, using cache`);
1950
- return {
1951
- ...cachedResult,
1952
- elapsedTimeMs: getElapsedTimeMs(),
1953
- reportIssueOptions: {
1954
- ...cachedResult.reportIssueOptions,
1955
- ...reportIssueOptions
1956
- }
1957
- };
1958
- }
1959
- const result = {
1960
- fileInfo: { filename },
1961
- issues: [],
1962
- processed: false,
1963
- errors: 0,
1964
- configErrors: 0,
1965
- elapsedTimeMs: 0,
1966
- reportIssueOptions
2676
+ result: fetch$1().catch((e) => toApplicationError(e))
1967
2677
  };
1968
- const fileInfo = prefetch$1?.fileInfo || await readFileInfo(filename, void 0, true);
1969
- if (fileInfo.errorCode) {
1970
- if (fileInfo.errorCode !== "EISDIR" && cfg.options.mustFindFiles) {
1971
- const err = new LinterError(`File not found: "${filename}"`);
1972
- reporter.error("Linter:", err);
1973
- result.errors += 1;
1974
- }
1975
- return result;
1976
- }
1977
- const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
1978
- const { text } = fileInfo;
1979
- result.fileInfo = fileInfo;
1980
- let spellResult = {};
1981
- reporter.info(`Checking: ${filename}, File type: ${doc.languageId ?? "auto"}, Language: ${doc.locale ?? "default"}`, MessageTypes.Info);
1982
- try {
1983
- const { showSuggestions: generateSuggestions, validateDirectives, skipValidation } = cfg.options;
1984
- const numSuggestions = configInfo.config.numSuggestions ?? 5;
1985
- const r = await spellCheckDocument(doc, clean({
1986
- generateSuggestions,
1987
- numSuggestions,
1988
- validateDirectives,
1989
- skipValidation
1990
- }), configInfo.config);
1991
- spellResult = r;
1992
- result.processed = r.checked;
1993
- result.perf = r.perf ? { ...r.perf } : void 0;
1994
- result.issues = Text.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
1995
- } catch (e) {
1996
- reporter.error(`Failed to process "${filename}"`, toError(e));
1997
- result.errors += 1;
1998
- }
1999
- result.elapsedTimeMs = getElapsedTimeMs();
2000
- const config = spellResult.settingsUsed ?? {};
2001
- result.reportIssueOptions = mergeReportIssueOptions(spellResult.settingsUsed || configInfo.config, reportIssueOptions);
2002
- result.configErrors += await reportConfigurationErrors(config);
2003
- const elapsed$1 = result.elapsedTimeMs;
2004
- const dictionaries = config.dictionaries || [];
2005
- reporter.info(`Checked: ${filename}, File type: ${config.languageId}, Language: ${config.language} ... Issues: ${result.issues.length} ${elapsed$1.toFixed(2)}ms`, MessageTypes.Info);
2006
- reporter.info(`Config file Used: ${spellResult.localConfigFilepath || configInfo.source}`, MessageTypes.Info);
2007
- reporter.info(`Dictionaries Used: ${dictionaries.join(", ")}`, MessageTypes.Info);
2008
- if (cfg.options.debug) {
2009
- const { id: _id, name: _name, __imports, __importRef,...cfg$1 } = config;
2010
- const debugCfg = {
2011
- filename,
2012
- languageId: doc.languageId ?? cfg$1.languageId ?? "default",
2013
- config: {
2014
- ...cfg$1,
2015
- source: null
2016
- },
2017
- source: spellResult.localConfigFilepath
2018
- };
2019
- reporter.debug(JSON.stringify(debugCfg, void 0, 2));
2020
- }
2021
- const dep = calcDependencies(config);
2022
- await cache.setCachedLintResults(result, dep.files);
2023
- return result;
2024
- }
2025
- function mapIssue({ doc: _,...tdo }) {
2026
- const context = cfg.showContext ? extractContext(tdo, cfg.showContext) : void 0;
2027
- return clean({
2028
- ...tdo,
2029
- context
2030
- });
2031
2678
  }
2032
2679
  async function processFiles(files, configInfo, cacheSettings) {
2033
2680
  const fileCount = Array.isArray(files) ? files.length : void 0;
@@ -2054,6 +2701,7 @@ async function runLint(cfg) {
2054
2701
  const { filename, result: pFetchResult } = pf;
2055
2702
  const getElapsedTimeMs = getTimeMeasurer();
2056
2703
  const fetchResult = await pFetchResult;
2704
+ if (fetchResult instanceof Error) throw fetchResult;
2057
2705
  reporter.emitProgressBegin(filename, index, fileCount ?? index);
2058
2706
  if (fetchResult?.skip) return {
2059
2707
  filename,
@@ -2061,13 +2709,14 @@ async function runLint(cfg) {
2061
2709
  result: {
2062
2710
  ...emptyResult,
2063
2711
  fileInfo: { filename },
2064
- elapsedTimeMs: getElapsedTimeMs()
2712
+ elapsedTimeMs: getElapsedTimeMs(),
2713
+ skippedReason: fetchResult.skipReason
2065
2714
  }
2066
2715
  };
2067
2716
  return {
2068
2717
  filename,
2069
2718
  fileNum: index,
2070
- result: await processFile(filename, configInfo, cache, fetchResult)
2719
+ result: await processFile(filename, cache, fetchResult, getProcessFileOptions(configInfo))
2071
2720
  };
2072
2721
  }
2073
2722
  async function* loadAndProcessFiles() {
@@ -2082,6 +2731,7 @@ async function runLint(cfg) {
2082
2731
  const { filename, fileNum, result } = fileP;
2083
2732
  status.files += 1;
2084
2733
  status.cachedFiles = (status.cachedFiles || 0) + (result.cached ? 1 : 0);
2734
+ status.skippedFiles = (status.skippedFiles || 0) + (result.processed ? 0 : 1);
2085
2735
  const numIssues = reporter.emitProgressComplete(filename, fileNum, fileCount ?? fileNum, result);
2086
2736
  if (numIssues || result.errors) {
2087
2737
  status.filesWithIssues.add(relativeToCwd(filename, cfg.root));
@@ -2094,39 +2744,10 @@ async function runLint(cfg) {
2094
2744
  await cache.reconcile();
2095
2745
  return status;
2096
2746
  }
2097
- function calcDependencies(config) {
2098
- const { configFiles, dictionaryFiles } = extractDependencies(config);
2099
- return { files: [...configFiles, ...dictionaryFiles] };
2100
- }
2101
- async function reportConfigurationErrors(config) {
2102
- const errors = extractImportErrors(config);
2103
- let count = 0;
2104
- errors.forEach((ref) => {
2105
- const key = ref.error.toString();
2106
- if (configErrors.has(key)) return;
2107
- configErrors.add(key);
2108
- count += 1;
2109
- reporter.error("Configuration", ref.error);
2110
- });
2111
- (await getDictionary(config)).dictionaries.forEach((dict) => {
2112
- const dictErrors = dict.getErrors?.() || [];
2113
- const msg = `Dictionary Error with (${dict.name})`;
2114
- dictErrors.forEach((error) => {
2115
- const key = msg + error.toString();
2116
- if (configErrors.has(key)) return;
2117
- configErrors.add(key);
2118
- count += 1;
2119
- reporter.error(msg, error);
2120
- });
2121
- });
2122
- return count;
2123
- }
2124
- function countConfigErrors(configInfo) {
2125
- return reportConfigurationErrors(configInfo.config);
2126
- }
2127
2747
  async function run() {
2128
2748
  if (cfg.options.root) setEnvironmentVariable(ENV_CSPELL_GLOB_ROOT, cfg.root);
2129
2749
  const configInfo = await readConfig(cfg.configFile, cfg.root, cfg.options.stopConfigSearchAt);
2750
+ const processFileOptions = getProcessFileOptions(configInfo);
2130
2751
  if (cfg.options.defaultConfiguration !== void 0) configInfo.config.loadDefaultConfiguration = cfg.options.defaultConfiguration;
2131
2752
  configInfo.config = mergeSettings(configInfo.config, cfg.cspellSettingsFromCliOptions);
2132
2753
  const reporterConfig = clean({
@@ -2139,15 +2760,15 @@ async function runLint(cfg) {
2139
2760
  const reporters = cfg.options.reporter ?? configInfo.config.reporters;
2140
2761
  reporter.config = reporterConfig;
2141
2762
  await reporter.loadReportersAndFinalize(reporters);
2142
- setLogger(getLoggerFromReporter(reporter));
2763
+ setLogger(getLoggerFromReporter(reporter, useColor));
2143
2764
  const globInfo = await determineGlobs(configInfo, cfg);
2144
2765
  const { fileGlobs, excludeGlobs } = globInfo;
2145
2766
  const hasFileLists = !!cfg.fileLists.length;
2146
2767
  if (!fileGlobs.length && !hasFileLists && !cfg.files?.length) return runResult();
2147
2768
  header(fileGlobs, excludeGlobs);
2148
2769
  checkGlobs(fileGlobs, reporter);
2149
- reporter.info(`Config Files Found:\n ${configInfo.source}\n`, MessageTypes.Info);
2150
- const configErrors$1 = await countConfigErrors(configInfo);
2770
+ if (verboseLevel > 1) reporter.info(`Config Files Found:\n ${relativeToCwd(configInfo.source)}\n`, MessageTypes$1.Info);
2771
+ const configErrors$1 = await countConfigErrors(configInfo, processFileOptions);
2151
2772
  if (configErrors$1 && cfg.options.exitCode !== false && !cfg.options.continueOnError) return runResult({ errors: configErrors$1 });
2152
2773
  const { root } = cfg;
2153
2774
  try {
@@ -2165,18 +2786,30 @@ async function runLint(cfg) {
2165
2786
  }
2166
2787
  }
2167
2788
  function header(files, cliExcludes) {
2789
+ if (verboseLevel < 2) return;
2168
2790
  const formattedFiles = files.length > 100 ? [...files.slice(0, 100), "..."] : files;
2169
- reporter.info(`
2170
- cspell;
2171
- Date: ${(/* @__PURE__ */ new Date()).toUTCString()}
2172
- Options:
2173
- verbose: ${yesNo(!!cfg.options.verbose)}
2174
- config: ${cfg.configFile || "default"}
2175
- exclude: ${cliExcludes.join("\n ")}
2176
- files: ${formattedFiles}
2177
- wordsOnly: ${yesNo(!!cfg.options.wordsOnly)}
2178
- unique: ${yesNo(!!cfg.options.unique)}
2179
- `, MessageTypes.Info);
2791
+ reporter.info(unindent`
2792
+ cspell;
2793
+ Date: ${(/* @__PURE__ */ new Date()).toUTCString()}
2794
+ Options:
2795
+ verbose: ${yesNo(!!cfg.options.verbose)}
2796
+ config: ${cfg.configFile || "default"}
2797
+ exclude: ${wordWrapAnsiText(cliExcludes.join(", "), 60, " ")}
2798
+ files: ${formattedFiles}
2799
+ wordsOnly: ${yesNo(!!cfg.options.wordsOnly)}
2800
+ unique: ${yesNo(!!cfg.options.unique)}
2801
+ `, MessageTypes$1.Info);
2802
+ }
2803
+ function getProcessFileOptions(configInfo) {
2804
+ return {
2805
+ reporter,
2806
+ chalk,
2807
+ configInfo,
2808
+ cfg,
2809
+ verboseLevel,
2810
+ useColor,
2811
+ configErrors
2812
+ };
2180
2813
  }
2181
2814
  }
2182
2815
  function checkGlobs(globs, reporter) {
@@ -2208,7 +2841,7 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2208
2841
  const globsToExclude = globsToExcludeRaw.filter((g) => !globPattern(g).startsWith("!"));
2209
2842
  if (globsToExclude.length !== globsToExcludeRaw.length) {
2210
2843
  const msg = `Negative glob exclusions are not supported: ${globsToExcludeRaw.map((g) => globPattern(g)).filter((g) => g.startsWith("!")).join(", ")}`;
2211
- reporter.info(msg, MessageTypes.Warning);
2844
+ reporter.info(msg, MessageTypes$1.Warning);
2212
2845
  }
2213
2846
  const globMatcher = buildGlobMatcher(globsToExclude, root, true);
2214
2847
  const globOptions = {
@@ -2234,13 +2867,13 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2234
2867
  const r = globMatcherExclude.matchEx(absFilename);
2235
2868
  if (r.matched) {
2236
2869
  const { glob: glob$2, source } = extractGlobSource(r.pattern);
2237
- reporter.info(`Excluded File: ${path$1.relative(root, absFilename)}; Excluded by ${glob$2} from ${source}`, MessageTypes.Info);
2870
+ if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Excluded File: ${path$1.relative(root, absFilename)}; Excluded by ${glob$2} from ${source}`, MessageTypes$1.Info);
2238
2871
  }
2239
2872
  return r.matched;
2240
2873
  }
2241
2874
  function filterOutExcludedFilesFn(globMatcherExclude) {
2242
2875
  const excludeInfo = globMatcherExclude.patterns.map(extractGlobSource).map(({ glob: glob$2, source }) => `Glob: ${glob$2} from ${source}`).filter(uniqueFn());
2243
- reporter.info(`Exclusion Globs: \n ${excludeInfo.join("\n ")}\n`, MessageTypes.Info);
2876
+ if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Exclusion Globs: \n ${excludeInfo.join("\n ")}\n`, MessageTypes$1.Info);
2244
2877
  return (filename) => !isExcluded(filename, globMatcherExclude);
2245
2878
  }
2246
2879
  return _determineFilesToCheck();
@@ -2265,13 +2898,14 @@ function runResult(init = {}) {
2265
2898
  function yesNo(value) {
2266
2899
  return value ? "Yes" : "No";
2267
2900
  }
2268
- function getLoggerFromReporter(reporter) {
2901
+ function getLoggerFromReporter(reporter, useColor) {
2902
+ const inspectOptions = { colors: useColor };
2269
2903
  const log = (...params) => {
2270
- const msg = format(...params);
2904
+ const msg = formatWithOptions(inspectOptions, ...params);
2271
2905
  reporter.info(msg, "Info");
2272
2906
  };
2273
2907
  const error = (...params) => {
2274
- const msg = format(...params);
2908
+ const msg = formatWithOptions(inspectOptions, ...params);
2275
2909
  reporter.error(msg, {
2276
2910
  message: "",
2277
2911
  name: "error",
@@ -2279,7 +2913,7 @@ function getLoggerFromReporter(reporter) {
2279
2913
  });
2280
2914
  };
2281
2915
  const warn = (...params) => {
2282
- const msg = format(...params);
2916
+ const msg = formatWithOptions(inspectOptions, ...params);
2283
2917
  reporter.info(msg, "Warning");
2284
2918
  };
2285
2919
  return {
@@ -2319,20 +2953,22 @@ async function* concatAsyncIterables(...iterables) {
2319
2953
  }
2320
2954
  async function writeDictionaryLog() {
2321
2955
  const fields = (getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOG_FIELDS") || "time, word, value").split(",").map((f) => f.trim());
2322
- const data = fields.join(", ") + "\n" + _debug.cacheDictionaryGetLog().filter((d) => d.method === "has").map((d) => fields.map((f) => f in d ? `${d[f]}` : "").join(", ")).join("\n") + "\n";
2956
+ const data = fields.join(", ") + "\n" + dictionaryCacheGetLog().filter((d) => d.method === "has").map((d) => fields.map((f) => f in d ? `${d[f]}` : "").join(", ")).join("\n") + "\n";
2323
2957
  await writeFileOrStream(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOG_FILE") || "cspell-dictionary-log.csv", data);
2324
2958
  }
2325
2959
  function globPattern(g) {
2326
2960
  return typeof g === "string" ? g : g.glob;
2327
2961
  }
2328
- var LinterError = class extends Error {
2329
- constructor(message) {
2330
- super(message);
2331
- }
2332
- toString() {
2333
- return this.message;
2334
- }
2335
- };
2962
+ function calcVerboseLevel(options) {
2963
+ return options.verboseLevel ?? (options.verbose ? 1 : 0);
2964
+ }
2965
+ function processMaxFileSize(value) {
2966
+ if (!value) return void 0;
2967
+ if (typeof value === "number") return value;
2968
+ const num = sizeToNumber(value);
2969
+ if (Number.isNaN(num)) throw new ApplicationError(`Invalid max file size: "${value}"`);
2970
+ return num;
2971
+ }
2336
2972
 
2337
2973
  //#endregion
2338
2974
  //#region src/lint/LintRequest.ts
@@ -2347,6 +2983,7 @@ var LintRequest = class {
2347
2983
  fileLists;
2348
2984
  files;
2349
2985
  cspellSettingsFromCliOptions;
2986
+ maxFileSize;
2350
2987
  constructor(fileGlobs, options, reporter) {
2351
2988
  this.fileGlobs = fileGlobs;
2352
2989
  this.options = options;
@@ -2370,6 +3007,7 @@ var LintRequest = class {
2370
3007
  ...extractUnknownWordsConfig(options),
2371
3008
  languageSettings
2372
3009
  };
3010
+ this.maxFileSize = options.maxFileSize ? sizeToNumber(options.maxFileSize) : void 0;
2373
3011
  }
2374
3012
  };
2375
3013
  function mergeFiles(a, b) {
@@ -2411,10 +3049,19 @@ const ReportChoicesAll = [
2411
3049
  "flagged"
2412
3050
  ];
2413
3051
  function fixLegacy(opts) {
2414
- const { local,...rest } = opts;
3052
+ const { local, ...rest } = opts;
2415
3053
  if (local && !rest.locale) rest.locale = local;
2416
3054
  return rest;
2417
3055
  }
3056
+ function cvtLinterCliCommandOptionsToLinterCliOptions(options) {
3057
+ const { verbose: verboseLevel, ...optionsRest } = options;
3058
+ const cliOptions = { ...optionsRest };
3059
+ if (verboseLevel) {
3060
+ cliOptions.verboseLevel = verboseLevel;
3061
+ cliOptions.verbose = true;
3062
+ }
3063
+ return cliOptions;
3064
+ }
2418
3065
 
2419
3066
  //#endregion
2420
3067
  //#region src/repl/index.ts
@@ -2655,5 +3302,5 @@ function parseApplicationFeatureFlags(flags) {
2655
3302
  }
2656
3303
 
2657
3304
  //#endregion
2658
- export { parseApplicationFeatureFlags as a, listDictionaries as c, npmPackage as d, getReporter as f, console as h, lint as i, ReportChoicesAll as l, CheckFailed as m, checkText as n, suggestions as o, ApplicationError as p, createInit as r, trace as s, IncludeExcludeFlag as t, DEFAULT_CACHE_LOCATION as u };
2659
- //# sourceMappingURL=application-CGIx5L0D.js.map
3305
+ export { CheckFailed as C, ApplicationError as S, padLeft as _, parseApplicationFeatureFlags as a, pruneAnsiTextStart as b, listDictionaries as c, validateUnitSize as d, unindent as f, tableToLines as g, getReporter as h, lint as i, ReportChoicesAll as l, npmPackage as m, checkText as n, suggestions as o, DEFAULT_CACHE_LOCATION as p, createInit as r, trace as s, IncludeExcludeFlag as t, cvtLinterCliCommandOptionsToLinterCliOptions as u, padWidth as v, console as w, width as x, pruneAnsiTextEnd as y };
3306
+ //# sourceMappingURL=application-BJHh60xM.js.map