cspell 9.1.3 → 9.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +29 -21
  2. package/bin.mjs +1 -1
  3. package/dist/esm/app.d.ts +19 -0
  4. package/dist/esm/app.js +1033 -0
  5. package/dist/esm/application-D-NwS6qb.js +2573 -0
  6. package/dist/esm/application-DbOQYm56.d.ts +116 -0
  7. package/dist/esm/application.d.ts +3 -0
  8. package/dist/esm/application.js +3 -0
  9. package/dist/esm/index.d.ts +55 -0
  10. package/dist/esm/index.js +5 -0
  11. package/dist/esm/options-ChaXtdFn.d.ts +387 -0
  12. package/package.json +27 -25
  13. package/dist/esm/app.d.mts +0 -5
  14. package/dist/esm/app.mjs +0 -34
  15. package/dist/esm/application.d.mts +0 -17
  16. package/dist/esm/application.mjs +0 -99
  17. package/dist/esm/cli-reporter.d.ts +0 -38
  18. package/dist/esm/cli-reporter.js +0 -386
  19. package/dist/esm/commandCheck.d.ts +0 -3
  20. package/dist/esm/commandCheck.js +0 -52
  21. package/dist/esm/commandConfig.d.ts +0 -3
  22. package/dist/esm/commandConfig.js +0 -18
  23. package/dist/esm/commandDictionaries.d.ts +0 -3
  24. package/dist/esm/commandDictionaries.js +0 -40
  25. package/dist/esm/commandHelpers.d.ts +0 -18
  26. package/dist/esm/commandHelpers.js +0 -30
  27. package/dist/esm/commandInit.d.ts +0 -3
  28. package/dist/esm/commandInit.js +0 -25
  29. package/dist/esm/commandLink.d.ts +0 -3
  30. package/dist/esm/commandLink.js +0 -48
  31. package/dist/esm/commandLint.d.ts +0 -3
  32. package/dist/esm/commandLint.js +0 -226
  33. package/dist/esm/commandSuggestion.d.ts +0 -3
  34. package/dist/esm/commandSuggestion.js +0 -61
  35. package/dist/esm/commandTrace.d.ts +0 -3
  36. package/dist/esm/commandTrace.js +0 -91
  37. package/dist/esm/config/adjustConfig.d.ts +0 -7
  38. package/dist/esm/config/adjustConfig.js +0 -137
  39. package/dist/esm/config/config.d.ts +0 -5
  40. package/dist/esm/config/config.js +0 -18
  41. package/dist/esm/config/configInit.d.ts +0 -3
  42. package/dist/esm/config/configInit.js +0 -104
  43. package/dist/esm/config/constants.d.ts +0 -17
  44. package/dist/esm/config/constants.js +0 -23
  45. package/dist/esm/config/index.d.ts +0 -3
  46. package/dist/esm/config/index.js +0 -2
  47. package/dist/esm/config/options.d.ts +0 -62
  48. package/dist/esm/config/options.js +0 -2
  49. package/dist/esm/config/updateConfig.d.ts +0 -3
  50. package/dist/esm/config/updateConfig.js +0 -2
  51. package/dist/esm/console.d.ts +0 -25
  52. package/dist/esm/console.js +0 -53
  53. package/dist/esm/dictionaries/index.d.ts +0 -3
  54. package/dist/esm/dictionaries/index.js +0 -2
  55. package/dist/esm/dictionaries/listDictionaries.d.ts +0 -33
  56. package/dist/esm/dictionaries/listDictionaries.js +0 -131
  57. package/dist/esm/dirname.d.ts +0 -2
  58. package/dist/esm/dirname.js +0 -13
  59. package/dist/esm/emitters/DictionaryPathFormat.d.ts +0 -3
  60. package/dist/esm/emitters/DictionaryPathFormat.js +0 -12
  61. package/dist/esm/emitters/dictionaryListEmitter.d.ts +0 -19
  62. package/dist/esm/emitters/dictionaryListEmitter.js +0 -82
  63. package/dist/esm/emitters/helpers.d.ts +0 -14
  64. package/dist/esm/emitters/helpers.js +0 -67
  65. package/dist/esm/emitters/suggestionsEmitter.d.ts +0 -13
  66. package/dist/esm/emitters/suggestionsEmitter.js +0 -79
  67. package/dist/esm/emitters/traceEmitter.d.ts +0 -19
  68. package/dist/esm/emitters/traceEmitter.js +0 -87
  69. package/dist/esm/environment.d.ts +0 -39
  70. package/dist/esm/environment.js +0 -30
  71. package/dist/esm/featureFlags/featureFlags.d.ts +0 -4
  72. package/dist/esm/featureFlags/featureFlags.js +0 -21
  73. package/dist/esm/featureFlags/index.d.ts +0 -2
  74. package/dist/esm/featureFlags/index.js +0 -2
  75. package/dist/esm/index.d.mts +0 -6
  76. package/dist/esm/index.mjs +0 -4
  77. package/dist/esm/link.d.ts +0 -8
  78. package/dist/esm/link.js +0 -39
  79. package/dist/esm/lint/LintRequest.d.ts +0 -26
  80. package/dist/esm/lint/LintRequest.js +0 -83
  81. package/dist/esm/lint/index.d.ts +0 -3
  82. package/dist/esm/lint/index.js +0 -3
  83. package/dist/esm/lint/lint.d.ts +0 -8
  84. package/dist/esm/lint/lint.js +0 -515
  85. package/dist/esm/models.d.ts +0 -15
  86. package/dist/esm/models.js +0 -2
  87. package/dist/esm/options.d.ts +0 -353
  88. package/dist/esm/options.js +0 -9
  89. package/dist/esm/pkgInfo.d.ts +0 -14
  90. package/dist/esm/pkgInfo.js +0 -7
  91. package/dist/esm/repl/index.d.ts +0 -18
  92. package/dist/esm/repl/index.js +0 -52
  93. package/dist/esm/util/InMemoryReporter.d.ts +0 -31
  94. package/dist/esm/util/InMemoryReporter.js +0 -49
  95. package/dist/esm/util/LintFileResult.d.ts +0 -14
  96. package/dist/esm/util/LintFileResult.js +0 -2
  97. package/dist/esm/util/async.d.ts +0 -3
  98. package/dist/esm/util/async.js +0 -4
  99. package/dist/esm/util/cache/CSpellLintResultCache.d.ts +0 -20
  100. package/dist/esm/util/cache/CSpellLintResultCache.js +0 -2
  101. package/dist/esm/util/cache/CacheOptions.d.ts +0 -34
  102. package/dist/esm/util/cache/CacheOptions.js +0 -2
  103. package/dist/esm/util/cache/DiskCache.d.ts +0 -63
  104. package/dist/esm/util/cache/DiskCache.js +0 -214
  105. package/dist/esm/util/cache/DummyCache.d.ts +0 -11
  106. package/dist/esm/util/cache/DummyCache.js +0 -18
  107. package/dist/esm/util/cache/ObjectCollection.d.ts +0 -17
  108. package/dist/esm/util/cache/ObjectCollection.js +0 -127
  109. package/dist/esm/util/cache/createCache.d.ts +0 -31
  110. package/dist/esm/util/cache/createCache.js +0 -69
  111. package/dist/esm/util/cache/file-entry-cache.d.mts +0 -4
  112. package/dist/esm/util/cache/file-entry-cache.mjs +0 -5
  113. package/dist/esm/util/cache/fileEntryCache.d.ts +0 -9
  114. package/dist/esm/util/cache/fileEntryCache.js +0 -79
  115. package/dist/esm/util/cache/index.d.ts +0 -4
  116. package/dist/esm/util/cache/index.js +0 -2
  117. package/dist/esm/util/canUseColor.d.ts +0 -2
  118. package/dist/esm/util/canUseColor.js +0 -10
  119. package/dist/esm/util/configFileHelper.d.ts +0 -15
  120. package/dist/esm/util/configFileHelper.js +0 -43
  121. package/dist/esm/util/constants.d.ts +0 -6
  122. package/dist/esm/util/constants.js +0 -6
  123. package/dist/esm/util/errors.d.ts +0 -24
  124. package/dist/esm/util/errors.js +0 -60
  125. package/dist/esm/util/extractContext.d.ts +0 -5
  126. package/dist/esm/util/extractContext.js +0 -75
  127. package/dist/esm/util/fileHelper.d.ts +0 -44
  128. package/dist/esm/util/fileHelper.js +0 -165
  129. package/dist/esm/util/glob.d.ts +0 -45
  130. package/dist/esm/util/glob.js +0 -147
  131. package/dist/esm/util/pad.d.ts +0 -45
  132. package/dist/esm/util/pad.js +0 -191
  133. package/dist/esm/util/prefetch.d.ts +0 -2
  134. package/dist/esm/util/prefetch.js +0 -15
  135. package/dist/esm/util/reporters.d.ts +0 -30
  136. package/dist/esm/util/reporters.js +0 -209
  137. package/dist/esm/util/stdin.d.ts +0 -2
  138. package/dist/esm/util/stdin.js +0 -5
  139. package/dist/esm/util/stdinUrl.d.ts +0 -9
  140. package/dist/esm/util/stdinUrl.js +0 -26
  141. package/dist/esm/util/table.d.ts +0 -41
  142. package/dist/esm/util/table.js +0 -115
  143. package/dist/esm/util/timer.d.ts +0 -4
  144. package/dist/esm/util/timer.js +0 -9
  145. package/dist/esm/util/types.d.ts +0 -7
  146. package/dist/esm/util/types.js +0 -5
  147. package/dist/esm/util/unindent.d.ts +0 -14
  148. package/dist/esm/util/unindent.js +0 -55
  149. package/dist/esm/util/util.d.ts +0 -14
  150. package/dist/esm/util/util.js +0 -30
  151. package/dist/esm/util/writeFile.d.ts +0 -3
  152. package/dist/esm/util/writeFile.js +0 -30
@@ -0,0 +1,1033 @@
1
+ import { ApplicationError, CheckFailed, DEFAULT_CACHE_LOCATION, IncludeExcludeFlag, ReportChoicesAll, checkText, console, createInit, lint, listDictionaries, npmPackage, parseApplicationFeatureFlags, suggestions, trace } from "./application-D-NwS6qb.js";
2
+ import { Option, program } from "commander";
3
+ import { satisfies } from "semver";
4
+ import chalk from "chalk";
5
+ import { Link } from "cspell-lib";
6
+ import assert from "node:assert";
7
+ import { stripVTControlCharacters } from "node:util";
8
+ import * as iPath from "node:path";
9
+
10
+ //#region src/commandCheck.ts
11
+ function commandCheck(prog) {
12
+ return prog.command("check <files...>").description("Spell check file(s) and display the result. The full file is displayed in color.").option("-c, --config <cspell.json>", "Configuration file to use. By default cspell looks for cspell.json in the current directory.").option("--validate-directives", "Validate in-document CSpell directives.").option("--no-validate-directives", "Do not validate in-document CSpell directives.").option("--no-color", "Turn off color.").option("--color", "Force color").option("--no-exit-code", "Do not return an exit code if issues are found.").addOption(new Option("--default-configuration", "Load the default configuration and dictionaries.").hideHelp()).addOption(new Option("--no-default-configuration", "Do not load the default configuration and dictionaries.")).action(async (files, options) => {
13
+ const useExitCode = options.exitCode ?? true;
14
+ parseApplicationFeatureFlags(options.flag);
15
+ let issueCount = 0;
16
+ for (const filename of files) {
17
+ console.log(chalk.yellowBright(`Check file: ${filename}`));
18
+ console.log();
19
+ try {
20
+ const result = await checkText(filename, options);
21
+ for (const item of result.items) {
22
+ const fn = item.flagIE === IncludeExcludeFlag.EXCLUDE ? chalk.gray : item.isError ? chalk.red : chalk.whiteBright;
23
+ const t = fn(item.text);
24
+ process.stdout.write(t);
25
+ issueCount += item.isError ? 1 : 0;
26
+ }
27
+ console.log();
28
+ } catch {
29
+ console.error(`File not found "${filename}"`);
30
+ throw new CheckFailed("File not found", 1);
31
+ }
32
+ console.log();
33
+ }
34
+ if (issueCount) {
35
+ const exitCode = useExitCode ?? true ? 1 : 0;
36
+ throw new CheckFailed("Issues found", exitCode);
37
+ }
38
+ });
39
+ }
40
+
41
+ //#endregion
42
+ //#region src/commandHelpers.ts
43
+ /**
44
+ * Collects string values into an array.
45
+ * @param value the new value(s) to collect.
46
+ * @param previous the previous values.
47
+ * @returns the new values appended to the previous values.
48
+ */
49
+ function collect(value, previous) {
50
+ const values = Array.isArray(value) ? value : [value];
51
+ return previous ? [...previous, ...values] : values;
52
+ }
53
+ /**
54
+ * Create Option - a helper function to create a commander option.
55
+ * @param name - the name of the option
56
+ * @param description - the description of the option
57
+ * @param parseArg - optional function to parse the argument
58
+ * @param defaultValue - optional default value
59
+ * @returns CommanderOption
60
+ */
61
+ function crOpt(name, description, parseArg, defaultValue) {
62
+ const option = new Option(name, description);
63
+ if (parseArg) option.argParser(parseArg);
64
+ if (defaultValue !== void 0) option.default(defaultValue);
65
+ return option;
66
+ }
67
+
68
+ //#endregion
69
+ //#region src/util/pad.ts
70
+ function pad(s, w) {
71
+ const p = padWidth(s, w);
72
+ if (!p) return s;
73
+ return s.padEnd(p + s.length);
74
+ }
75
+ function padWidth(s, target) {
76
+ const sWidth = ansiWidth(s);
77
+ return Math.max(target - sWidth, 0);
78
+ }
79
+ function padLeft(s, w) {
80
+ const p = padWidth(s, w);
81
+ if (!p) return s;
82
+ return s.padStart(p + s.length);
83
+ }
84
+ function isAnsiString(s) {
85
+ return s.includes("\x1B") || s.includes("›");
86
+ }
87
+ function width(s) {
88
+ assert(!s.includes("\x1B"), "String contains ANSI control characters");
89
+ return s.replaceAll(/[\u0000-\u001F\u0300-\u036F]/g, "").replaceAll(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, ".").replaceAll("…", ".").replaceAll(/\p{M}/gu, "").length;
90
+ }
91
+ function ansiWidth(s) {
92
+ return width(stripVTControlCharacters(s));
93
+ }
94
+ /**
95
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
96
+ * @param str - the text to prune - ANSI is not supported
97
+ * @param maxWidth - the maximum width of the text
98
+ * @param pad - the string to use for padding, default is '…'
99
+ * @returns the pruned text
100
+ */
101
+ function pruneTextEnd(str, maxWidth$2, pad$1 = "…") {
102
+ if (!maxWidth$2 || maxWidth$2 <= 0) return str;
103
+ if (str.length <= maxWidth$2) return str;
104
+ if (isAnsiString(str)) return pruneAnsiTextEnd(str, maxWidth$2, pad$1);
105
+ const padWidth$1 = width(pad$1);
106
+ const maxWidthWithPad = maxWidth$2 - padWidth$1;
107
+ const letters = [...str];
108
+ let len = 0;
109
+ for (let i = 0; i < letters.length; i++) {
110
+ const c = letters[i];
111
+ len += width(c);
112
+ if (len > maxWidthWithPad) {
113
+ let j = i + 1;
114
+ while (j < letters.length && width(letters[j]) === 0) ++j;
115
+ return j === letters.length ? str : letters.slice(0, i).join("") + pad$1;
116
+ }
117
+ }
118
+ return str;
119
+ }
120
+ /**
121
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
122
+ * @param str - the text to prune - ANSI is not supported
123
+ * @param maxWidth - the maximum width of the text
124
+ * @param pad - the string to use for padding, default is '…'
125
+ * @returns the pruned text
126
+ */
127
+ function pruneTextStart(str, maxWidth$2, pad$1 = "…") {
128
+ if (!maxWidth$2 || maxWidth$2 <= 0) return str;
129
+ if (str.length <= maxWidth$2) return str;
130
+ const padWidth$1 = width(pad$1);
131
+ const maxWidthWithPad = maxWidth$2 - padWidth$1;
132
+ const letters = [...str];
133
+ let len = 0;
134
+ for (let i = letters.length - 1; i >= 1; i--) {
135
+ const c = letters[i];
136
+ len += width(c);
137
+ if (len > maxWidthWithPad) {
138
+ i += 1;
139
+ while (i < letters.length && width(letters[i]) === 0) ++i;
140
+ return pad$1 + letters.slice(i).join("");
141
+ }
142
+ }
143
+ return str;
144
+ }
145
+ const ansi = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", "g");
146
+ function parseAnsiStr(str) {
147
+ const fragments = [];
148
+ let lastIndex = 0;
149
+ for (const match of str.matchAll(ansi)) {
150
+ if (match.index > lastIndex) fragments.push({
151
+ type: "text",
152
+ text: str.slice(lastIndex, match.index)
153
+ });
154
+ fragments.push({
155
+ type: "ansi",
156
+ text: match[0]
157
+ });
158
+ lastIndex = match.index + match[0].length;
159
+ }
160
+ if (lastIndex < str.length) fragments.push({
161
+ type: "text",
162
+ text: str.slice(lastIndex)
163
+ });
164
+ return fragments;
165
+ }
166
+ /**
167
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
168
+ * @param str - the text to prune - ANSI is supported
169
+ * @param maxWidth - the maximum width of the text
170
+ * @param pad - the string to use for padding, default is '…'
171
+ * @returns the pruned text
172
+ */
173
+ function pruneAnsiTextEnd(str, maxWidth$2, pad$1 = "…") {
174
+ if (!maxWidth$2 || maxWidth$2 <= 0) return str;
175
+ if (str.length <= maxWidth$2) return str;
176
+ if (ansiWidth(str) <= maxWidth$2) return str;
177
+ const padWidth$1 = ansiWidth(pad$1);
178
+ const fragments = parseAnsiStr(str);
179
+ const maxWidthWithPad = maxWidth$2 - padWidth$1;
180
+ let remaining = maxWidthWithPad;
181
+ for (const frag of fragments) {
182
+ if (frag.type !== "text") continue;
183
+ if (remaining <= 0) {
184
+ frag.text = "";
185
+ continue;
186
+ }
187
+ const pruned = pruneTextEnd(frag.text, remaining, pad$1);
188
+ if (pruned !== frag.text) {
189
+ frag.text = pruned;
190
+ remaining = 0;
191
+ continue;
192
+ }
193
+ remaining -= width(frag.text);
194
+ }
195
+ return fragments.map((frag) => frag.text).join("");
196
+ }
197
+ /**
198
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
199
+ * @param str - the text to prune - ANSI is supported
200
+ * @param maxWidth - the maximum width of the text
201
+ * @param pad - the string to use for padding, default is '…'
202
+ * @returns the pruned text
203
+ */
204
+ function pruneAnsiTextStart(str, maxWidth$2, pad$1 = "…") {
205
+ if (!maxWidth$2 || maxWidth$2 <= 0) return str;
206
+ if (str.length <= maxWidth$2) return str;
207
+ if (ansiWidth(str) <= maxWidth$2) return str;
208
+ const padWidth$1 = ansiWidth(pad$1);
209
+ const fragments = parseAnsiStr(str);
210
+ const maxWidthWithPad = maxWidth$2 - padWidth$1;
211
+ let remaining = maxWidthWithPad;
212
+ for (const frag of fragments.reverse()) {
213
+ if (frag.type !== "text") continue;
214
+ if (remaining <= 0) {
215
+ frag.text = "";
216
+ continue;
217
+ }
218
+ const pruned = pruneTextStart(frag.text, remaining, pad$1);
219
+ if (pruned !== frag.text) {
220
+ frag.text = pruned;
221
+ remaining = 0;
222
+ continue;
223
+ }
224
+ remaining -= width(frag.text);
225
+ }
226
+ return fragments.reverse().map((frag) => frag.text).join("");
227
+ }
228
+
229
+ //#endregion
230
+ //#region src/util/table.ts
231
+ function tableToLines(table, deliminator) {
232
+ const del = deliminator || table.deliminator || " | ";
233
+ const columnWidths = [];
234
+ const maxColumnWidthsMap = table.maxColumnWidths || {};
235
+ const { header, rows } = table;
236
+ const simpleHeader = header.map((col) => Array.isArray(col) ? col[1] : col);
237
+ const columnFieldNames = header.map((col) => Array.isArray(col) ? col[0] : col);
238
+ const maxColumnWidths = columnFieldNames.map((field, idx) => maxColumnWidthsMap[field] ?? maxColumnWidthsMap[idx]);
239
+ function getCell(row, col) {
240
+ return getCellFromRow(rows[row], col);
241
+ }
242
+ function getCellFromRow(row, col) {
243
+ if (!row) return void 0;
244
+ if (Array.isArray(row)) return row[col];
245
+ const fieldName = columnFieldNames[col];
246
+ return row[fieldName];
247
+ }
248
+ function rowToCells(row) {
249
+ if (Array.isArray(row)) return row;
250
+ return columnFieldNames.map((fieldName) => row[fieldName]);
251
+ }
252
+ function getText(col, maxWidth$2) {
253
+ return !col ? "" : typeof col === "string" ? pruneTextEnd(col, maxWidth$2) : col(maxWidth$2);
254
+ }
255
+ function getRCText(row, col, maxWidth$2) {
256
+ return getText(getCell(row, col), maxWidth$2);
257
+ }
258
+ function recordHeaderWidths(header$1) {
259
+ header$1.forEach((col, idx) => {
260
+ columnWidths[idx] = Math.max(ansiWidth(col), columnWidths[idx] || 0);
261
+ });
262
+ }
263
+ function recordColWidths() {
264
+ 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);
265
+ }
266
+ function justifyRow(c, i) {
267
+ return pad(c, columnWidths[i]);
268
+ }
269
+ function toHeaderLine(header$1) {
270
+ return decorateRowWith(header$1.map((c, i) => getText(c, columnWidths[i])), justifyRow, headerDecorator).join(del);
271
+ }
272
+ function toLine(row) {
273
+ return decorateRowWith(rowToCells(row).map((c, i) => getText(c, columnWidths[i])), justifyRow).join(del);
274
+ }
275
+ function* process$1() {
276
+ yield toHeaderLine(simpleHeader);
277
+ yield* rows.map(toLine);
278
+ }
279
+ function sumColumnWidths() {
280
+ return columnWidths.reduce((sum, width$1) => sum + width$1, 0);
281
+ }
282
+ function adjustColWidths() {
283
+ for (let i = 0; i < columnWidths.length; i++) {
284
+ const mw = maxColumnWidths[i];
285
+ if (!mw) continue;
286
+ columnWidths[i] = Math.min(columnWidths[i], mw);
287
+ }
288
+ if (!table.terminalWidth) return;
289
+ const dWidth = (columnWidths.length - 1) * ansiWidth(del);
290
+ const lineWidth = table.terminalWidth - dWidth;
291
+ if (lineWidth <= columnWidths.length * 2) {
292
+ const fixedWidth = Math.max(Math.min(...columnWidths), 5);
293
+ for (let i = 0; i < columnWidths.length; i++) columnWidths[i] = fixedWidth;
294
+ return;
295
+ }
296
+ if (columnWidths.length === 1) {
297
+ columnWidths[0] = lineWidth;
298
+ return;
299
+ }
300
+ function trimWidestColumn(neededToTrim) {
301
+ let first = 0;
302
+ let second = 0;
303
+ for (let i = 0; i < columnWidths.length; i++) if (columnWidths[i] > columnWidths[first]) {
304
+ second = first;
305
+ first = i;
306
+ } else if (columnWidths[i] > columnWidths[second]) second = i;
307
+ const diff$1 = Math.max(columnWidths[first] - columnWidths[second], 1);
308
+ columnWidths[first] -= Math.min(diff$1, neededToTrim);
309
+ }
310
+ for (let sum = sumColumnWidths(); sum > lineWidth; sum = sumColumnWidths()) trimWidestColumn(sum - lineWidth);
311
+ }
312
+ recordHeaderWidths(simpleHeader);
313
+ recordColWidths();
314
+ adjustColWidths();
315
+ return [...process$1()];
316
+ }
317
+ function headerDecorator(t) {
318
+ return chalk.bold(chalk.underline(t));
319
+ }
320
+ function decorateRowWith(row, ...decorators) {
321
+ return decorators.reduce((row$1, decorator) => row$1.map(decorator), row);
322
+ }
323
+
324
+ //#endregion
325
+ //#region src/emitters/helpers.ts
326
+ function trimMidPath(s, w, sep) {
327
+ if (s.length <= w) return s;
328
+ const parts = s.split(sep);
329
+ if (parts[parts.length - 1].length > w) return trimMid(s, w);
330
+ function join(left$1, right$1) {
331
+ return [
332
+ ...parts.slice(0, left$1),
333
+ "…",
334
+ ...parts.slice(right$1)
335
+ ].join(sep);
336
+ }
337
+ let left = 0, right = parts.length, last = "";
338
+ for (let i = 0; i < parts.length; ++i) {
339
+ const incLeft = i & 1 ? 1 : 0;
340
+ const incRight = incLeft ? 0 : -1;
341
+ const next = join(left + incLeft, right + incRight);
342
+ if (next.length > w) break;
343
+ left += incLeft;
344
+ right += incRight;
345
+ last = next;
346
+ }
347
+ for (let i = left + 1; i < right; ++i) {
348
+ const next = join(i, right);
349
+ if (next.length > w) break;
350
+ last = next;
351
+ }
352
+ for (let i = right - 1; i > left; --i) {
353
+ const next = join(left, i);
354
+ if (next.length > w) break;
355
+ last = next;
356
+ }
357
+ return last || trimMid(s, w);
358
+ }
359
+ function trimMid(s, w) {
360
+ s = s.trim();
361
+ if (s.length <= w) return s;
362
+ const l = Math.floor((w - 1) / 2);
363
+ const r = Math.ceil((w - 1) / 2);
364
+ return s.slice(0, l) + "…" + s.slice(-r);
365
+ }
366
+ function formatDictionaryLocation(dictSource, maxWidth$2, { cwd, dictionaryPathFormat: format$1, iPath: iPath$1 }) {
367
+ let relPath = cwd ? iPath$1.relative(cwd, dictSource) : dictSource;
368
+ const idxNodeModule = relPath.lastIndexOf("node_modules");
369
+ const isNodeModule = idxNodeModule >= 0;
370
+ if (format$1 === "hide") return "";
371
+ if (format$1 === "short") {
372
+ const prefix = isNodeModule ? "[node_modules]/" : relPath.startsWith(".." + iPath$1.sep + "..") ? "…/" : relPath.startsWith(".." + iPath$1.sep) ? "../" : "";
373
+ return prefix + iPath$1.basename(dictSource);
374
+ }
375
+ if (format$1 === "full") return dictSource;
376
+ relPath = isNodeModule ? relPath.slice(idxNodeModule) : relPath;
377
+ const usePath = relPath.length < dictSource.length ? relPath : dictSource;
378
+ return trimMidPath(usePath, maxWidth$2, iPath$1.sep);
379
+ }
380
+
381
+ //#endregion
382
+ //#region src/emitters/dictionaryListEmitter.ts
383
+ const maxWidth$1 = 120;
384
+ function emitListDictionariesResults(results, options) {
385
+ const report = calcListDictsResultsReport(results, options);
386
+ console.log(report.table);
387
+ if (report.errors) {
388
+ console.error("Errors:");
389
+ console.error(report.errors);
390
+ }
391
+ }
392
+ function calcListDictsResultsReport(results, options) {
393
+ if (options.color === true) chalk.level = 2;
394
+ else if (options.color === false) chalk.level = 0;
395
+ const col = new Intl.Collator();
396
+ results.sort((a, b) => col.compare(a.name, b.name));
397
+ const header = calcHeaders(options);
398
+ const rows = results.map((r) => dictTableRowToTableRow(emitDictResult(r, options)));
399
+ const t = tableToLines({
400
+ header,
401
+ rows,
402
+ terminalWidth: options.lineWidth || process.stdout.columns || maxWidth$1,
403
+ deliminator: " ",
404
+ maxColumnWidths: {
405
+ locales: 12,
406
+ fileTypes: 40
407
+ }
408
+ });
409
+ return {
410
+ table: t.map((line) => line.trimEnd()).join("\n"),
411
+ errors: ""
412
+ };
413
+ }
414
+ function calcHeaders(options) {
415
+ const showLocation = options.dictionaryPathFormat !== "hide" && (options.options.showLocation ?? true);
416
+ const showLocales = options.options.showLocales ?? true;
417
+ const showFileTypes = options.options.showFileTypes ?? true;
418
+ const headers = [["name", "Dictionary"]];
419
+ showLocales && headers.push(["locales", "Locales"]);
420
+ showFileTypes && headers.push(["fileTypes", "File Types"]);
421
+ showLocation && headers.push(["location", "Dictionary Location"]);
422
+ return headers;
423
+ }
424
+ function emitDictResult(r, options) {
425
+ const a = r.enabled ? "*" : " ";
426
+ const dictColor = r.enabled ? chalk.yellowBright : chalk.rgb(200, 128, 50);
427
+ const n = (width$1) => dictColor(pruneAnsiTextEnd(r.name, width$1 && width$1 - a.length) + a);
428
+ const c = colorize$1(chalk.white);
429
+ const locales = (width$1) => c(pruneAnsiTextEnd(r.locales?.join(",") || "", width$1));
430
+ const fileTypes = (width$1) => c(pruneAnsiTextEnd(r.fileTypes?.join(",") || "", width$1));
431
+ if (!r.path) return {
432
+ name: n,
433
+ location: c(r.inline?.join(", ") || ""),
434
+ locales,
435
+ fileTypes
436
+ };
437
+ return {
438
+ name: n,
439
+ location: (widthSrc) => c(r.path && pruneAnsiTextStart(formatDictionaryLocation(r.path, widthSrc ?? maxWidth$1, {
440
+ iPath,
441
+ ...options
442
+ }), widthSrc ?? maxWidth$1) || ""),
443
+ locales,
444
+ fileTypes
445
+ };
446
+ }
447
+ function dictTableRowToTableRow(row) {
448
+ return Object.fromEntries(Object.entries(row));
449
+ }
450
+ function colorize$1(fn) {
451
+ return (s) => s ? fn(s) : "";
452
+ }
453
+
454
+ //#endregion
455
+ //#region src/emitters/DictionaryPathFormat.ts
456
+ const formats = {
457
+ full: true,
458
+ hide: true,
459
+ long: true,
460
+ short: true
461
+ };
462
+ function isDictionaryPathFormat(value) {
463
+ if (!value || typeof value !== "string") return false;
464
+ return value in formats;
465
+ }
466
+
467
+ //#endregion
468
+ //#region src/util/canUseColor.ts
469
+ function canUseColor(colorOption) {
470
+ if (colorOption !== void 0) return colorOption;
471
+ if (!("NO_COLOR" in process.env)) return void 0;
472
+ if (!process.env["NO_COLOR"] || process.env["NO_COLOR"] === "false") return void 0;
473
+ return false;
474
+ }
475
+
476
+ //#endregion
477
+ //#region src/commandDictionaries.ts
478
+ function commandDictionaries(prog) {
479
+ return prog.command("dictionaries").description(`List dictionaries`).option("-c, --config <cspell.json>", "Configuration file to use. By default cspell looks for cspell.json in the current directory.").addOption(crOpt("--path-format <format>", "Configure how to display the dictionary path.").choices([
480
+ "hide",
481
+ "short",
482
+ "long",
483
+ "full"
484
+ ]).default("long", "Display most of the path.")).addOption(crOpt("--enabled", "Show only enabled dictionaries.").default(void 0)).addOption(crOpt("--no-enabled", "Do not show enabled dictionaries.")).option("--locale <locale>", "Set language locales. i.e. \"en,fr\" for English and French, or \"en-GB\" for British English.").option("--file-type <fileType>", "File type to use. i.e. \"html\", \"golang\", or \"javascript\".").option("--no-show-location", "Do not show the location of the dictionary.").option("--show-file-types", "Show the file types supported by the dictionary.", false).addOption(crOpt("--no-show-file-types", "Do not show the file types supported by the dictionary.").hideHelp()).option("--show-locales", "Show the language locales supported by the dictionary.", false).addOption(crOpt("--no-show-locales", "Do not show the locales supported by the dictionary.").hideHelp()).addOption(crOpt("--color", "Force color.").default(void 0)).addOption(crOpt("--no-color", "Turn off color.").default(void 0)).addOption(crOpt("--default-configuration", "Load the default configuration and dictionaries.").hideHelp()).addOption(crOpt("--no-default-configuration", "Do not load the default configuration and dictionaries.")).action(async (options) => {
485
+ const dictionaryPathFormat = isDictionaryPathFormat(options.pathFormat) ? options.pathFormat : "long";
486
+ const useColor = canUseColor(options.color);
487
+ const listResult = await listDictionaries(options);
488
+ emitListDictionariesResults(listResult, {
489
+ cwd: process.cwd(),
490
+ dictionaryPathFormat,
491
+ color: useColor,
492
+ options
493
+ });
494
+ });
495
+ }
496
+
497
+ //#endregion
498
+ //#region src/commandInit.ts
499
+ function commandInit(prog) {
500
+ const command = prog.command("init").description("Initialize a CSpell configuration file.").addOption(crOpt("-c, --config <path>", "Path to the CSpell configuration file. Conflicts with --output and --format.").conflicts(["output", "format"])).option("-o, --output <path>", "Define where to write file.").addOption(crOpt("--format <format>", "Define the format of the file.").choices([
501
+ "yaml",
502
+ "yml",
503
+ "json",
504
+ "jsonc"
505
+ ]).default("yaml")).option("--import <path|package>", "Import a configuration file or dictionary package.", collect).option("--locale <locale>", "Define the locale to use when spell checking (e.g., en, en-US, de).").addOption(crOpt("--dictionary <dictionary>", "Enable a dictionary. Can be used multiple times.", collect).default(void 0)).addOption(crOpt("--comments", "Add comments to the config file.").default(void 0).hideHelp()).option("--no-comments", "Do not add comments to the config file.").addOption(crOpt("--remove-comments", "Remove all comments from the config file.").implies({ comments: false })).option("--no-schema", "Do not add the schema reference to the config file.").option("--stdout", "Write the configuration to stdout instead of a file.").action((options) => {
506
+ return createInit(options);
507
+ });
508
+ return command;
509
+ }
510
+
511
+ //#endregion
512
+ //#region src/link.ts
513
+ const listGlobalImports = Link.listGlobalImports;
514
+ const addPathsToGlobalImports = Link.addPathsToGlobalImports;
515
+ const removePathsFromGlobalImports = Link.removePathsFromGlobalImports;
516
+ function listGlobalImportsResultToTable(results) {
517
+ const header = [
518
+ "id",
519
+ "package",
520
+ "name",
521
+ "filename",
522
+ "dictionaries",
523
+ "errors"
524
+ ];
525
+ const decorate = (isError) => isError ? (s) => chalk.red(s) : (s) => s;
526
+ function toColumns(r) {
527
+ return [
528
+ r.id,
529
+ r.package?.name,
530
+ r.name,
531
+ r.filename,
532
+ r.dictionaryDefinitions?.map((def) => def.name).join(", "),
533
+ r.error ? "Failed to read file." : ""
534
+ ].map((c) => c || "").map(decorate(!!r.error));
535
+ }
536
+ return {
537
+ header,
538
+ rows: results.map(toColumns)
539
+ };
540
+ }
541
+ function addPathsToGlobalImportsResultToTable(results) {
542
+ const header = ["filename", "errors"];
543
+ const decorate = (isError) => isError ? (s) => chalk.red(s) : (s) => s;
544
+ function toColumns(r) {
545
+ return [r.resolvedToFilename || r.filename, r.error ? "Failed to read file." : ""].map((c) => c || "").map(decorate(!!r.error));
546
+ }
547
+ return {
548
+ header,
549
+ rows: results.resolvedSettings.map(toColumns)
550
+ };
551
+ }
552
+
553
+ //#endregion
554
+ //#region src/commandLink.ts
555
+ function commandLink(prog) {
556
+ const linkCommand = prog.command("link").description("Link dictionaries and other settings to the cspell global config.");
557
+ linkCommand.command("list", { isDefault: true }).alias("ls").description("List currently linked configurations.").action(async () => {
558
+ const imports = await listGlobalImports();
559
+ const table = listGlobalImportsResultToTable(imports.list);
560
+ tableToLines(table).forEach((line) => console.log(line));
561
+ return;
562
+ });
563
+ linkCommand.command("add <dictionaries...>").alias("a").description("Add dictionaries any other settings to the cspell global config.").action(async (dictionaries) => {
564
+ const r = await addPathsToGlobalImports(dictionaries);
565
+ const table = addPathsToGlobalImportsResultToTable(r);
566
+ console.log("Adding:");
567
+ tableToLines(table).forEach((line) => console.log(line));
568
+ if (r.error) throw new CheckFailed(r.error, 1);
569
+ return;
570
+ });
571
+ linkCommand.command("remove <paths...>").alias("r").description("Remove matching paths / packages from the global config.").action(async (dictionaries) => {
572
+ const r = await removePathsFromGlobalImports(dictionaries);
573
+ console.log("Removing:");
574
+ if (r.error) throw new CheckFailed(r.error, 1);
575
+ r.removed.map((f) => console.log(f));
576
+ return;
577
+ });
578
+ return linkCommand;
579
+ }
580
+
581
+ //#endregion
582
+ //#region src/util/unindent.ts
583
+ /**
584
+ * Inject values into a template string.
585
+ * @param {TemplateStringsArray} template
586
+ * @param {...any} values
587
+ * @returns
588
+ */
589
+ function _inject(template, ...values) {
590
+ const strings = template;
591
+ const adjValues = [];
592
+ for (let i = 0; i < values.length; ++i) {
593
+ const prevLines = strings[i].split("\n");
594
+ const currLine = prevLines[prevLines.length - 1];
595
+ const padLen = padLength(currLine);
596
+ const padding = " ".repeat(padLen);
597
+ const value = `${values[i]}`;
598
+ let pad$1 = "";
599
+ const valueLines = [];
600
+ for (const line of value.split("\n")) {
601
+ valueLines.push(pad$1 + line);
602
+ pad$1 = padding;
603
+ }
604
+ adjValues.push(valueLines.join("\n"));
605
+ }
606
+ return _unindent(String.raw({ raw: strings }, ...adjValues));
607
+ }
608
+ /**
609
+ * Calculate the padding at the start of the string.
610
+ * @param {string} s
611
+ * @returns {number}
612
+ */
613
+ function padLength(s) {
614
+ return s.length - s.trimStart().length;
615
+ }
616
+ function unindent(template, ...values) {
617
+ if (typeof template === "string") return _unindent(template);
618
+ return _inject(template, ...values);
619
+ }
620
+ /**
621
+ * Remove the left padding from a multi-line string.
622
+ * @param {string} str
623
+ * @returns {string}
624
+ */
625
+ function _unindent(str) {
626
+ const lines = str.split("\n");
627
+ let curPad = str.length;
628
+ for (const line of lines) {
629
+ if (!line.trim()) continue;
630
+ curPad = Math.min(curPad, padLength(line));
631
+ }
632
+ return lines.map((line) => line.slice(curPad)).join("\n");
633
+ }
634
+
635
+ //#endregion
636
+ //#region src/commandLint.ts
637
+ const usage = `\
638
+ [options] [globs...] [file://<path> ...] [stdin[://<path>]]
639
+
640
+ Patterns:
641
+ - [globs...] Glob Patterns
642
+ - [stdin] Read from "stdin" assume text file.
643
+ - [stdin://<path>] Read from "stdin", use <path> for file type and config.
644
+ - [file://<path>] Check the file at <path>
645
+
646
+ Examples:
647
+ cspell . Recursively check all files.
648
+ cspell lint . The same as "cspell ."
649
+ cspell "*.js" Check all .js files in the current directory
650
+ cspell "**/*.js" Check all .js files recursively
651
+ cspell "src/**/*.js" Only check .js under src
652
+ cspell "**/*.txt" "**/*.js" Check both .js and .txt files.
653
+ cspell "**/*.{txt,js,md}" Check .txt, .js, and .md files.
654
+ cat LICENSE | cspell stdin Check stdin
655
+ cspell stdin://docs/doc.md Check stdin as if it was "./docs/doc.md"\
656
+ `;
657
+ const advanced = `
658
+ More Examples:
659
+
660
+ cspell "**/*.js" --reporter @cspell/cspell-json-reporter
661
+ This will spell check all ".js" files recursively and use
662
+ "@cspell/cspell-json-reporter".
663
+
664
+ cspell . --reporter default
665
+ This will force the default reporter to be used overriding
666
+ any reporters defined in the configuration.
667
+
668
+ cspell . --reporter ./<path>/reporter.cjs
669
+ Use a custom reporter. See API for details.
670
+
671
+ cspell "*.md" --exclude CHANGELOG.md --files README.md CHANGELOG.md
672
+ Spell check only check "README.md" but NOT "CHANGELOG.md".
673
+
674
+ cspell "/*.md" --no-must-find-files --files $FILES
675
+ Only spell check the "/*.md" files in $FILES,
676
+ where $FILES is a shell variable that contains the list of files.
677
+
678
+ cspell --help --verbose
679
+ Show all options including hidden options.
680
+
681
+ References:
682
+ https://cspell.org
683
+ https://github.com/streetsidesoftware/cspell
684
+ `;
685
+ function commandLint(prog) {
686
+ const spellCheckCommand = prog.command("lint", { isDefault: true });
687
+ spellCheckCommand.description("Check spelling").option("-c, --config <cspell.json>", "Configuration file to use. By default cspell looks for cspell.json in the current directory.").addOption(crOpt("--config-search", "Allow searching for configuration files.", void 0).hideHelp()).option("--no-config-search", "Disable automatic searching for additional configuration files in parent directories. Only the specified config file (if any) will be used.").option("--stop-config-search-at <dir>", "Specify a directory at which to stop searching for configuration files when walking up from the files being checked. Useful for limiting config inheritance.", collect).option("-v, --verbose", "Display more information about the files being checked and the configuration.").option("--locale <locale>", "Set language locales. i.e. \"en,fr\" for English and French, or \"en-GB\" for British English.").option("--language-id <file-type>", "Force programming language for unknown extensions. i.e. \"php\" or \"scala\"").addOption(crOpt("--languageId <file-type>", "Alias of \"--language-id\". Force programming language for unknown extensions. i.e. \"php\" or \"scala\"").hideHelp()).option("--words-only", "Only output the words not found in the dictionaries.").addOption(crOpt("--wordsOnly", "Only output the words not found in the dictionaries.").hideHelp()).option("-u, --unique", "Only output the first instance of a word not found in the dictionaries.").option("-e, --exclude <glob>", "Exclude files matching the glob pattern. This option can be used multiple times to add multiple globs. ", collect).option("--file-list <path or stdin>", "Specify a list of files to be spell checked. The list is filtered against the glob file patterns. Note: the format is 1 file path per line.", collect).option("--file [file...]", "Specify files to spell check. They are filtered by the [globs...].", collect).addOption(crOpt("--files [file...]", "Alias of \"--file\". Files to spell check.", collect).hideHelp()).option("--no-issues", "Do not show the spelling errors.").option("--no-progress", "Turn off progress messages").option("--no-summary", "Turn off summary message in console.").option("-s, --silent", "Silent mode, suppress error messages.").option("--no-exit-code", "Do not return an exit code if issues are found.").addOption(crOpt("--quiet", "Only show spelling issues or errors.").implies({
688
+ summary: false,
689
+ progress: false
690
+ })).option("--fail-fast", "Exit after first file with an issue or error.").addOption(crOpt("--no-fail-fast", "Process all files even if there is an error.").hideHelp()).option("--continue-on-error", "Continue processing files even if there is a configuration error.").option("-r, --root <root folder>", "Root directory, defaults to current directory.").addOption(crOpt("--relative", "Issues are displayed relative to the root.").default(true).hideHelp()).option("--no-relative", "Issues are displayed with absolute path instead of relative to the root.").option("--show-context", "Show the surrounding text around an issue.").option("--show-suggestions", "Show spelling suggestions.").addOption(crOpt("--no-show-suggestions", "Do not show spelling suggestions or fixes.").default(void 0)).addOption(crOpt("--must-find-files", "Error if no files are found.").default(true).hideHelp()).option("--no-must-find-files", "Do not error if no files are found.").addOption(crOpt("--legacy", "Legacy output").hideHelp()).addOption(crOpt("--local <local>", "Deprecated -- Use: --locale").hideHelp()).option("--cache", "Use cache to only check changed files.").option("--no-cache", "Do not use cache.").option("--cache-reset", "Reset the cache file.").addOption(crOpt("--cache-strategy <strategy>", "Strategy to use for detecting changed files.").choices(["content", "metadata"]).default("content")).option("--cache-location <path>", `Path to the cache file or directory. (default: "${DEFAULT_CACHE_LOCATION}")`).option("--dot", "Include files and directories starting with `.` (period) when matching globs.").option("--gitignore", "Ignore files matching glob patterns found in .gitignore files.").option("--no-gitignore", "Do NOT use .gitignore files.").option("--gitignore-root <path>", "Prevent searching for .gitignore files past root.", collect).option("--validate-directives", "Validate in-document CSpell directives.").addOption(crOpt("--no-validate-directives", "Do not validate in-document CSpell directives.").hideHelp()).addOption(crOpt("--color", "Force color.").default(void 0)).addOption(crOpt("--no-color", "Turn off color.").default(void 0)).addOption(crOpt("--default-configuration", "Load the default configuration and dictionaries.").hideHelp()).addOption(crOpt("--no-default-configuration", "Do not load the default configuration and dictionaries.")).option("--dictionary <name>", "Enable a dictionary by name.", collect).option("--disable-dictionary <name>", "Disable a dictionary by name.", collect).option("--reporter <module|path>", "Specify one or more reporters to use.", collect).addOption(crOpt("--report <level>", "Set how unknown words are reported").choices(ReportChoicesAll)).addOption(crOpt("--skip-validation", "Collect and process documents, but do not spell check.").implies({ cache: false }).hideHelp()).addOption(crOpt("--issues-summary-report", "Output a summary of issues found.").hideHelp()).addOption(crOpt("--show-perf-summary", "Output a performance summary report.").hideHelp()).option("--issue-template [template]", "Use a custom issue template. See --help --issue-template for details.").addOption(crOpt("--debug", "Output information useful for debugging cspell.json files.").hideHelp()).usage(usage).addHelpText("after", augmentCommandHelp).arguments("[globs...]").action(async (fileGlobs, options) => {
691
+ const useExitCode = options.exitCode ?? true;
692
+ if (options.skipValidation) options.cache = false;
693
+ options.color ??= canUseColor(options.color);
694
+ parseApplicationFeatureFlags(options.flag);
695
+ const { mustFindFiles, fileList, files, file } = options;
696
+ const result = await lint(fileGlobs, options);
697
+ if (!fileGlobs.length && !result.files && !result.errors && !fileList && !files?.length && !file?.length) {
698
+ spellCheckCommand.outputHelp();
699
+ throw new CheckFailed("outputHelp", 1);
700
+ }
701
+ if (result.errors || mustFindFiles && !result.files) throw new CheckFailed("check failed", 1);
702
+ if (result.issues) {
703
+ const exitCode = useExitCode ? 1 : 0;
704
+ throw new CheckFailed("check failed", exitCode);
705
+ }
706
+ return;
707
+ });
708
+ return spellCheckCommand;
709
+ }
710
+ function helpIssueTemplate(opts) {
711
+ if (!("issueTemplate" in opts)) return "";
712
+ return unindent`
713
+ Issue Template:
714
+ Use "--issue-template" to set the template to use when reporting issues.
715
+
716
+ The template is a string that can contain the following placeholders:
717
+ - $filename - the file name
718
+ - $col - the column number
719
+ - $row - the row number
720
+ - $text - the word that is misspelled
721
+ - $message - the issues message: "unknown word", "word is misspelled", etc.
722
+ - $messageColored - the issues message with color based upon the message type.
723
+ - $uri - the URI of the file
724
+ - $suggestions - suggestions for the misspelled word (if requested)
725
+ - $quickFix - possible quick fixes for the misspelled word.
726
+ - $contextFull - the full context of the misspelled word.
727
+ - $contextLeft - the context to the left of the misspelled word.
728
+ - $contextRight - the context to the right of the misspelled word.
729
+
730
+ Color is supported using the following template pattern:
731
+ - \`{<style[.style]> <text>}\` - where \`<style>\` is a style name and \`<text>\` is the text to style.
732
+
733
+ Styles
734
+ - \`bold\`, \`italic\`, \`underline\`, \`strikethrough\`, \`dim\`, \`inverse\`
735
+ - \`black\`, \`red\`, \`green\`, \`yellow\`, \`blue\`, \`magenta\`, \`cyan\`, \`white\`
736
+
737
+ Example:
738
+ --issue-template '{green $filename}:{yellow $row}:{yellow $col} $message {red $text} $quickFix {dim $suggestions}'
739
+ `;
740
+ }
741
+ /**
742
+ * Add additional help text to the command.
743
+ * When the verbose flag is set, show the hidden options.
744
+ * @param context
745
+ * @returns
746
+ */
747
+ function augmentCommandHelp(context) {
748
+ const output = [];
749
+ const command = context.command;
750
+ const opts = command.opts();
751
+ const showHidden = !!opts.verbose;
752
+ const hiddenHelp = [];
753
+ const help = command.createHelp();
754
+ help.helpWidth = process.stdout.columns || 80;
755
+ const hiddenOptions = command.options.filter((opt) => opt.hidden && showHidden);
756
+ const flagColWidth = Math.max(...command.options.map((opt) => opt.flags.length), 0);
757
+ for (const options of hiddenOptions) {
758
+ if (!hiddenHelp.length) hiddenHelp.push("\nHidden Options:");
759
+ hiddenHelp.push(help.formatItem(options.flags, flagColWidth, options.description, help));
760
+ }
761
+ output.push(...hiddenHelp, advanced);
762
+ return helpIssueTemplate(opts) + output.join("\n");
763
+ }
764
+
765
+ //#endregion
766
+ //#region src/emitters/suggestionsEmitter.ts
767
+ const regExpRTL = /([ \u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]+)/g;
768
+ function reverseRtlText(s) {
769
+ return s.replaceAll(regExpRTL, (s$1) => [...s$1].reverse().join(""));
770
+ }
771
+ function emitSuggestionResult(result, options) {
772
+ const { word, suggestions: suggestions$1 } = result;
773
+ const { verbose, output = console } = options;
774
+ const elapsed = verbose && verbose > 1 && result.elapsedTimeMs ? ` ${result.elapsedTimeMs.toFixed(2)} ms` : "";
775
+ const rWord = reverseRtlText(word);
776
+ const wordEx = rWord !== word ? ` (${chalk.yellow(rWord)})` : "";
777
+ output.log((word ? chalk.yellow(word) + wordEx : chalk.yellow("<empty>")) + ":" + elapsed);
778
+ if (!suggestions$1.length) {
779
+ console.log(chalk.yellow(" <no suggestions>"));
780
+ return;
781
+ }
782
+ function handleRtl(word$1) {
783
+ const r = reverseRtlText(word$1);
784
+ return r === word$1 ? word$1 : `${word$1} (${r})`;
785
+ }
786
+ if (verbose) {
787
+ const mappedSugs = suggestions$1.map((s) => ({
788
+ ...s,
789
+ w: handleRtl(s.compoundWord || s.wordAdjustedToMatchCase || s.word)
790
+ }));
791
+ const sugWidths = mappedSugs.map((s) => width(s.w));
792
+ const maxWidth$2 = sugWidths.reduce((max, len) => Math.max(max, len), 0);
793
+ for (const sug of mappedSugs) {
794
+ const { cost, dictionaries, w } = sug;
795
+ const padding = " ".repeat(padWidth(w, maxWidth$2));
796
+ const forbid = sug.forbidden && sug.isPreferred ? chalk.red("*") : sug.forbidden ? chalk.red("X") : sug.isPreferred ? chalk.yellow("*") : " ";
797
+ const ignore = sug.noSuggest ? chalk.yellow("N") : " ";
798
+ const strCost = padLeft(cost.toString(10), 4);
799
+ const dicts = dictionaries.map((n) => chalk.gray(n)).join(", ");
800
+ output.log(` - ${formatWord(w, sug)}${padding} ${forbid}${ignore} - ${chalk.yellow(strCost)} ${dicts}`);
801
+ }
802
+ } else {
803
+ const mappedSugs = suggestions$1.map((s) => ({
804
+ ...s,
805
+ word: handleRtl(s.wordAdjustedToMatchCase || s.word)
806
+ }));
807
+ for (const r of mappedSugs) output.log(` - ${formatWordSingle(r)}`);
808
+ }
809
+ }
810
+ function formatWord(word, r) {
811
+ return r.forbidden || r.noSuggest ? chalk.gray(chalk.strikethrough(word)) : word === r.wordAdjustedToMatchCase ? diff(word, r.word) : word;
812
+ }
813
+ function diff(wordA, wordB) {
814
+ const a = [...wordA];
815
+ const b = [...wordB];
816
+ const parts = [];
817
+ for (let idx = 0; idx < a.length; ++idx) {
818
+ const aa = a[idx];
819
+ const bb = b[idx];
820
+ parts.push(aa === bb ? aa : chalk.yellow(aa));
821
+ }
822
+ return parts.join("");
823
+ }
824
+ function formatWordSingle(s) {
825
+ let word = formatWord(s.word, s);
826
+ word = s.forbidden ? word + chalk.red(" X") : word;
827
+ word = s.noSuggest ? word + chalk.yellow(" Not suggested.") : word;
828
+ word = s.isPreferred ? chalk.yellow(word + " *") : word;
829
+ return word;
830
+ }
831
+
832
+ //#endregion
833
+ //#region src/commandSuggestion.ts
834
+ function collect$1(value, previous) {
835
+ value = value.replace(/^=/, "");
836
+ if (!previous) return [value];
837
+ return [...previous, value];
838
+ }
839
+ function count(_, previous) {
840
+ return (previous || 0) + 1;
841
+ }
842
+ function asNumber(value, prev) {
843
+ return Number.parseInt(value, 10) ?? prev;
844
+ }
845
+ function commandSuggestion(prog) {
846
+ const suggestionCommand = prog.command("suggestions");
847
+ suggestionCommand.aliases(["sug", "suggest"]).description("Spelling Suggestions for words.").option("-c, --config <cspell.json>", "Configuration file to use. By default cspell looks for cspell.json in the current directory.").option("--locale <locale>", "Set language locales. i.e. \"en,fr\" for English and French, or \"en-GB\" for British English.").option("--language-id <language>", "Use programming language. i.e. \"php\" or \"scala\".").addOption(new Option("--languageId <language>", "Use programming language. i.e. \"php\" or \"scala\".").hideHelp()).option("-s, --no-strict", "Ignore case and accents when searching for words.").option("--ignore-case", "Alias of --no-strict.").option("--num-changes <number>", "Number of changes allowed to a word", asNumber, 4).option("--num-suggestions <number>", "Number of suggestions", asNumber, 8).option("--no-include-ties", "Force the number of suggested to be limited, by not including suggestions that have the same edit cost.").option("--stdin", "Use stdin for input.").addOption(new Option("--repl", "REPL interface for looking up suggestions.")).option("-v, --verbose", "Show detailed output.", count, 0).option("-d, --dictionary <dictionary name>", "Use the dictionary specified. Only dictionaries specified will be used.", collect$1).option("--dictionaries <dictionary names...>", "Use the dictionaries specified. Only dictionaries specified will be used.").option("--no-color", "Turn off color.").option("--color", "Force color").arguments("[words...]").action(async (words, options) => {
848
+ parseApplicationFeatureFlags(options.flag);
849
+ options.useStdin = options.stdin;
850
+ options.dictionaries = mergeArrays(options.dictionaries, options.dictionary);
851
+ if (!words.length && !options.useStdin && !options.repl) {
852
+ suggestionCommand.outputHelp();
853
+ throw new CheckFailed("outputHelp", 1);
854
+ }
855
+ for await (const r of suggestions(words, options)) emitSuggestionResult(r, options);
856
+ });
857
+ return suggestionCommand;
858
+ }
859
+ function mergeArrays(a, b) {
860
+ if (a === void 0) return b;
861
+ if (b === void 0) return a;
862
+ return [...a, ...b];
863
+ }
864
+
865
+ //#endregion
866
+ //#region src/emitters/traceEmitter.ts
867
+ const maxWidth = 120;
868
+ const colWidthDictionaryName = 20;
869
+ function emitTraceResults(word, found, results, options) {
870
+ const report = calcTraceResultsReport(word, found, results, options);
871
+ console.log(report.table);
872
+ if (report.errors) {
873
+ console.error("Errors:");
874
+ console.error(report.errors);
875
+ }
876
+ }
877
+ function calcTraceResultsReport(word, found, results, options) {
878
+ if (options.color === true) chalk.level = 2;
879
+ else if (options.color === false) chalk.level = 0;
880
+ const col = new Intl.Collator();
881
+ results.sort((a, b) => col.compare(a.dictName, b.dictName));
882
+ options.showWordFound && console.log(`${options.prefix || ""}${word}: ${found ? "Found" : "Not Found"}`);
883
+ const header = emitHeader(options.dictionaryPathFormat !== "hide");
884
+ const rows = results.map((r) => emitTraceResult(r, options));
885
+ const t = tableToLines({
886
+ header,
887
+ rows,
888
+ terminalWidth: options.lineWidth || process.stdout.columns || maxWidth,
889
+ deliminator: " "
890
+ });
891
+ return {
892
+ table: t.map((line) => line.trimEnd()).join("\n"),
893
+ errors: emitErrors(results).join("\n")
894
+ };
895
+ }
896
+ function emitHeader(location) {
897
+ const headers = [
898
+ "Word",
899
+ "F",
900
+ "Dictionary"
901
+ ];
902
+ location && headers.push("Dictionary Location");
903
+ return headers;
904
+ }
905
+ function emitTraceResult(r, options) {
906
+ const errors = !!r.errors?.length;
907
+ const word = r.foundWord || r.word;
908
+ const cWord = word.replaceAll("+", chalk.yellow("+"));
909
+ const sug = r.preferredSuggestions?.map((s) => chalk.yellowBright(s)).join(", ") || "";
910
+ const w = (r.forbidden ? chalk.red(cWord) : chalk.green(cWord)) + (sug ? `->(${sug})` : "");
911
+ const f = calcFoundChar(r);
912
+ const a = r.dictActive ? "*" : " ";
913
+ const dictName = r.dictName.slice(0, colWidthDictionaryName - 1) + a;
914
+ const dictColor = r.dictActive ? chalk.yellowBright : chalk.rgb(200, 128, 50);
915
+ const n = dictColor(dictName);
916
+ const c = colorize(errors ? chalk.red : chalk.white);
917
+ return [
918
+ w,
919
+ f,
920
+ n,
921
+ (widthSrc) => c(formatDictionaryLocation(r.dictSource, widthSrc ?? maxWidth, {
922
+ iPath,
923
+ ...options
924
+ }))
925
+ ];
926
+ }
927
+ function emitErrors(results) {
928
+ const errorResults = results.filter((r) => r.errors?.length);
929
+ return errorResults.map((r) => {
930
+ const errors = r.errors?.map((e) => e.message)?.join("\n ") || "";
931
+ return chalk.bold(r.dictName) + "\n " + chalk.red(errors);
932
+ });
933
+ }
934
+ function calcFoundChar(r) {
935
+ const errors = r.errors?.map((e) => e.message)?.join("\n ") || "";
936
+ let color = chalk.dim;
937
+ color = r.found ? chalk.whiteBright : color;
938
+ color = r.forbidden ? chalk.red : color;
939
+ color = r.noSuggest ? chalk.yellowBright : color;
940
+ color = errors ? chalk.red : color;
941
+ let char = "-";
942
+ char = r.found ? "*" : char;
943
+ char = r.forbidden ? "!" : char;
944
+ char = r.noSuggest ? "I" : char;
945
+ char = errors ? "X" : char;
946
+ return color(char);
947
+ }
948
+ function colorize(fn) {
949
+ return (s) => s ? fn(s) : "";
950
+ }
951
+
952
+ //#endregion
953
+ //#region src/commandTrace.ts
954
+ function commandTrace(prog) {
955
+ return prog.command("trace").description(`Trace words -- Search for words in the configuration and dictionaries.`).option("-c, --config <cspell.json>", "Configuration file to use. By default cspell looks for cspell.json in the current directory.").option("--locale <locale>", "Set language locales. i.e. \"en,fr\" for English and French, or \"en-GB\" for British English.").option("--language-id <language>", "Use programming language. i.e. \"php\" or \"scala\".").addOption(new Option("--languageId <language>", "Use programming language. i.e. \"php\" or \"scala\".").hideHelp()).option("--allow-compound-words", "Turn on allowCompoundWords").addOption(new Option("--allowCompoundWords", "Turn on allowCompoundWords.").hideHelp()).option("--no-allow-compound-words", "Turn off allowCompoundWords").option("--ignore-case", "Ignore case and accents when searching for words.").option("--no-ignore-case", "Do not ignore case and accents when searching for words.").option("--dictionary <name>", "Enable a dictionary by name. Can be used multiple times.", collect).addOption(new Option("--dictionary-path <format>", "Configure how to display the dictionary path.").choices([
956
+ "hide",
957
+ "short",
958
+ "long",
959
+ "full"
960
+ ]).default("long", "Display most of the path.")).option("--stdin", "Read words from stdin.").option("--all", "Show all dictionaries.").addOption(new Option("--only-found", "Show only dictionaries that have the words.").conflicts("all")).addOption(new Option("--color", "Force color.").default(void 0)).addOption(new Option("--no-color", "Turn off color.").default(void 0)).addOption(new Option("--default-configuration", "Load the default configuration and dictionaries.").hideHelp()).addOption(new Option("--no-default-configuration", "Do not load the default configuration and dictionaries.")).arguments("[words...]").action(async (words, options) => {
961
+ parseApplicationFeatureFlags(options.flag);
962
+ let numFound = 0;
963
+ const dictionaryPathFormat = isDictionaryPathFormat(options.dictionaryPath) ? options.dictionaryPath : "long";
964
+ let prefix = "";
965
+ const useColor = canUseColor(options.color);
966
+ for await (const results of trace(words, options)) {
967
+ const byWord = groupBy(results, (r) => r.word);
968
+ for (const split of results.splits) {
969
+ const splitResults = byWord.get(split.word) || [];
970
+ const filtered = filterTraceResults(splitResults, options);
971
+ emitTraceResults(split.word, split.found, filtered, {
972
+ cwd: process.cwd(),
973
+ dictionaryPathFormat,
974
+ prefix,
975
+ showWordFound: results.splits.length > 1,
976
+ color: useColor
977
+ });
978
+ prefix = "\n";
979
+ numFound += results.reduce((n, r) => n + (r.found ? 1 : 0), 0);
980
+ const numErrors = results.map((r) => r.errors?.length || 0).reduce((n, r) => n + r, 0);
981
+ if (numErrors) {
982
+ console.error("Dictionary Errors.");
983
+ throw new CheckFailed("dictionary errors", 1);
984
+ }
985
+ }
986
+ }
987
+ if (!numFound) {
988
+ console.error("No matches found");
989
+ throw new CheckFailed("no matches", 1);
990
+ }
991
+ });
992
+ }
993
+ function filterTraceResults(results, options) {
994
+ if (options.all) return results;
995
+ return results.filter((r) => filterTraceResult(r, options.onlyFound));
996
+ }
997
+ function filterTraceResult(result, onlyFound) {
998
+ return result.found || result.forbidden || result.noSuggest || !!result.preferredSuggestions || !onlyFound && result.dictActive;
999
+ }
1000
+ function groupBy(items, key) {
1001
+ const map = /* @__PURE__ */ new Map();
1002
+ for (const item of items) {
1003
+ const k = key(item);
1004
+ const a = map.get(k) || [];
1005
+ a.push(item);
1006
+ map.set(k, a);
1007
+ }
1008
+ return map;
1009
+ }
1010
+
1011
+ //#endregion
1012
+ //#region src/app.mts
1013
+ async function run(command, argv) {
1014
+ const prog = command || program;
1015
+ const args = argv || process.argv;
1016
+ prog.exitOverride();
1017
+ prog.version(npmPackage.version).description("Spelling Checker for Code").name("cspell");
1018
+ if (!satisfies(process.versions.node, npmPackage.engines.node)) throw new ApplicationError(`Unsupported NodeJS version (${process.versions.node}); ${npmPackage.engines.node} is required`);
1019
+ const optionFlags = new Option("-f,--flag <flag:value>", "Declare an execution flag value").hideHelp().argParser((value, prev) => prev?.concat(value) || [value]);
1020
+ commandLint(prog).addOption(optionFlags);
1021
+ commandTrace(prog).addOption(optionFlags);
1022
+ commandCheck(prog).addOption(optionFlags);
1023
+ commandSuggestion(prog).addOption(optionFlags);
1024
+ commandInit(prog).addOption(optionFlags);
1025
+ commandLink(prog);
1026
+ commandDictionaries(prog);
1027
+ prog.exitOverride();
1028
+ await prog.parseAsync(args);
1029
+ }
1030
+
1031
+ //#endregion
1032
+ export { ApplicationError, CheckFailed, run };
1033
+ //# sourceMappingURL=app.js.map