@cspell/eslint-plugin 9.4.0 → 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.
package/README.md CHANGED
@@ -96,7 +96,16 @@ interface Options {
96
96
  * Generate suggestions
97
97
  * @default true
98
98
  */
99
- generateSuggestions: boolean;
99
+ generateSuggestions?: boolean;
100
+ /**
101
+ * Control the reporting level for unknown words.
102
+ * - `'all'` — report all unknown words.
103
+ * - `'simple'` — report unknown words except for more complex or compound cases.
104
+ * - `'typos'` — focus on words that are likely simple typographical errors.
105
+ * - `'flagged'` — only report words that match flagged terms.
106
+ * @default 'all'
107
+ */
108
+ report?: 'all' | 'simple' | 'typos' | 'flagged';
100
109
  /**
101
110
  * Ignore import, require names, and export from names
102
111
  * @default true
@@ -49,7 +49,7 @@ function prefixLines(text, prefix, startIndex = 1) {
49
49
  return text
50
50
  .split('\n')
51
51
  .map((line, index) => (index >= startIndex ? prefix + line : line))
52
- .map((line) => (line.trim() == '' ? '' : line))
52
+ .map((line) => (line.trim() === '' ? '' : line))
53
53
  .join('\n');
54
54
  }
55
55
  //# sourceMappingURL=logger.cjs.map
@@ -7,9 +7,8 @@ export interface Options extends Check {
7
7
  numSuggestions: number;
8
8
  /**
9
9
  * Generate suggestions
10
- * @default true
11
10
  */
12
- generateSuggestions: boolean;
11
+ generateSuggestions?: boolean;
13
12
  /**
14
13
  * Automatically fix common mistakes.
15
14
  * This is only possible if a single preferred suggestion is available.
@@ -21,6 +20,17 @@ export interface Options extends Check {
21
20
  * default false
22
21
  */
23
22
  debugMode?: boolean;
23
+ /**
24
+ * Reporting level for unknown words
25
+ *
26
+ * - 'all' - Report all unknown words (default)
27
+ * - 'simple' - Report unknown words with simple suggestions and flagged words
28
+ * - 'typos' - Report only common typos and flagged words
29
+ * - 'flagged' - Report only flagged words
30
+ *
31
+ * default is 'all' unless overridden by CSpell settings
32
+ */
33
+ report?: 'all' | 'simple' | 'typos' | 'flagged' | undefined;
24
34
  }
25
35
  interface DictOptions {
26
36
  /**
@@ -38,7 +48,7 @@ type DictionaryDefinition = (DictBase & Pick<DictionaryDefinitionPreferred, 'pat
38
48
  export type CSpellOptions = Pick<CSpellSettings, 'allowCompoundWords' | 'caseSensitive' | 'dictionaries' | 'enabled' | 'flagWords' | 'ignoreWords' | 'ignoreRegExpList' | 'includeRegExpList' | 'import' | 'language' | 'words'> & {
39
49
  dictionaryDefinitions?: DictionaryDefinition[];
40
50
  };
41
- export type RequiredOptions = Required<Pick<Options, Exclude<keyof Options, 'debugMode'>>> & Pick<Options, 'debugMode'>;
51
+ export type RequiredOptions = Options & Required<Pick<Options, Exclude<keyof Options, 'debugMode' | 'report' | 'generateSuggestions'>>>;
42
52
  export interface Check {
43
53
  /**
44
54
  * Ignore import and require names
@@ -148,7 +158,7 @@ export interface CustomWordListFile {
148
158
  export type WorkerOptions = RequiredOptions & {
149
159
  cwd: string;
150
160
  };
151
- export declare const defaultOptions: Options;
161
+ export declare const defaultOptions: Options & Required<Pick<Options, 'generateSuggestions' | 'numSuggestions' | 'autoFix'>>;
152
162
  /**
153
163
  * The scope selector is a string that defines the context in which a rule applies.
154
164
  * Examples:
@@ -127,8 +127,8 @@ exports.optionsSchema = {
127
127
  "type": "string"
128
128
  },
129
129
  "path": {
130
- "description": "Path to the file.",
131
- "markdownDescription": "Path to the file.",
130
+ "description": "Path or url to the dictionary file.",
131
+ "markdownDescription": "Path or url to the dictionary file.",
132
132
  "type": "string"
133
133
  },
134
134
  "supportNonStrictSearches": {
@@ -409,7 +409,6 @@ exports.optionsSchema = {
409
409
  "type": "boolean"
410
410
  },
411
411
  "generateSuggestions": {
412
- "default": true,
413
412
  "description": "Generate suggestions",
414
413
  "markdownDescription": "Generate suggestions",
415
414
  "type": "boolean"
@@ -431,11 +430,21 @@ exports.optionsSchema = {
431
430
  "description": "Number of spelling suggestions to make.",
432
431
  "markdownDescription": "Number of spelling suggestions to make.",
433
432
  "type": "number"
433
+ },
434
+ "report": {
435
+ "description": "Reporting level for unknown words\n\n- 'all' - Report all unknown words (default)\n- 'simple' - Report unknown words with simple suggestions and flagged words\n- 'typos' - Report only common typos and flagged words\n- 'flagged' - Report only flagged words\n\n default is 'all' unless overridden by CSpell settings",
436
+ "enum": [
437
+ "all",
438
+ "simple",
439
+ "typos",
440
+ "flagged"
441
+ ],
442
+ "markdownDescription": "Reporting level for unknown words\n\n- 'all' - Report all unknown words (default)\n- 'simple' - Report unknown words with simple suggestions and flagged words\n- 'typos' - Report only common typos and flagged words\n- 'flagged' - Report only flagged words\n\n default is 'all' unless overridden by CSpell settings",
443
+ "type": "string"
434
444
  }
435
445
  },
436
446
  "required": [
437
447
  "numSuggestions",
438
- "generateSuggestions",
439
448
  "autoFix"
440
449
  ],
441
450
  "type": "object"
@@ -7,8 +7,11 @@ const schema_cjs_1 = require("../generated/schema.cjs");
7
7
  const index_cjs_1 = require("../spellCheckAST/index.cjs");
8
8
  const defaultCheckOptions_cjs_1 = require("./defaultCheckOptions.cjs");
9
9
  const messages = {
10
- wordUnknown: 'Unknown word: "{{word}}"',
11
10
  wordForbidden: 'Forbidden word: "{{word}}"',
11
+ wordForbiddenWithSuggestions: 'Forbidden word: "{{word}}" ({{suggestions}})',
12
+ wordMisspelled: 'Misspelled word: "{{word}}" ({{suggestions}})',
13
+ wordUnknown: 'Unknown word: "{{word}}"',
14
+ wordUnknownWithSuggestions: 'Unknown word: "{{word}}" ({{suggestions}})',
12
15
  suggestWord: '{{word}}{{preferred}}',
13
16
  };
14
17
  const ruleMeta = {
@@ -36,10 +39,24 @@ function create(context) {
36
39
  logger.enabled = options.debugMode ?? (logger.enabled || isDebugMode);
37
40
  logContext(log, context);
38
41
  function reportIssue(issue) {
39
- const messageId = issue.severity === 'Forbidden' ? 'wordForbidden' : 'wordUnknown';
42
+ const allowNonPreferred = options.report === 'simple' && issue.hasSimpleSuggestions;
43
+ const sugs = issue.suggestions
44
+ ?.filter((sug) => sug.isPreferred || allowNonPreferred)
45
+ .map((sug) => sug.wordAdjustedToMatchCase || sug.word);
46
+ let messageId = 'wordUnknown';
47
+ messageId = issue.hasSimpleSuggestions && sugs?.length ? 'wordUnknownWithSuggestions' : messageId;
48
+ messageId = issue.severity === 'Misspelled' ? 'wordMisspelled' : messageId;
49
+ messageId =
50
+ issue.severity === 'Forbidden'
51
+ ? issue.hasPreferredFixes
52
+ ? 'wordForbiddenWithSuggestions'
53
+ : 'wordForbidden'
54
+ : messageId;
55
+ const corrections = sugs?.join(', ') || 'no suggestions';
40
56
  const { word, start, end } = issue;
41
57
  const data = {
42
58
  word,
59
+ suggestions: corrections,
43
60
  };
44
61
  const code = contextSourceCode(context);
45
62
  const startPos = code.getLocFromIndex(start);
@@ -23,7 +23,6 @@ exports.defaultCheckOptions = {
23
23
  exports.defaultOptions = {
24
24
  ...exports.defaultCheckOptions,
25
25
  numSuggestions: 8,
26
- generateSuggestions: true,
27
26
  autoFix: false,
28
27
  };
29
28
  function normalizeOptions(opts, cwd) {
@@ -58,7 +58,7 @@ class AstScopeMatcher {
58
58
  return 0;
59
59
  if (curr.childKey && !item.childKey && matchKey)
60
60
  return 0;
61
- if (curr.childKey && (curr.childKey == item.childKey || !matchKey)) {
61
+ if (curr.childKey && (curr.childKey === item.childKey || !matchKey)) {
62
62
  score += scale;
63
63
  }
64
64
  score += scale * 2;
@@ -1,3 +1,4 @@
1
+ import type { UnknownWordsChoices } from 'cspell-lib';
1
2
  import type { Options } from '../common/options.cjs';
2
3
  export type SpellCheckOptions = Options & {
3
4
  cwd: string;
@@ -25,9 +26,13 @@ export interface SpellCheckIssue {
25
26
  /** the word that was flagged */
26
27
  word: string;
27
28
  /** the severity of the issue. */
28
- severity: 'Forbidden' | 'Unknown' | 'Hint';
29
+ severity: 'Forbidden' | 'Misspelled' | 'Unknown' | 'Hint';
29
30
  /** suggestions to be presented. */
30
31
  suggestions: Suggestions;
32
+ /** Indicates that there preferred suggestions. */
33
+ hasPreferredFixes: boolean;
34
+ /** Indicates that there are simple suggestions available. */
35
+ hasSimpleSuggestions: boolean;
31
36
  /** The range of text in which this issue occurred. */
32
37
  range: CheckTextRange;
33
38
  /** The index of the range in which this issues occurred. */
@@ -41,5 +46,10 @@ export type CheckTextRange = readonly [number, number];
41
46
  export type SpellCheckFn = typeof spellCheck;
42
47
  export type SpellCheckSyncFn = (...p: Parameters<SpellCheckFn>) => Awaited<ReturnType<SpellCheckFn>>;
43
48
  export declare function spellCheck(filename: string, text: string, ranges: CheckTextRange[], options: SpellCheckOptions): Promise<SpellCheckResults>;
49
+ export type ReportTypes = Exclude<Options['report'], undefined>;
50
+ type MapReportToUnknownWordChoices = {
51
+ [key in ReportTypes]: UnknownWordsChoices;
52
+ };
53
+ export declare const mapReportToUnknownWordChoices: MapReportToUnknownWordChoices;
44
54
  export {};
45
55
  //# sourceMappingURL=spellCheck.d.mts.map
@@ -4,6 +4,9 @@ import * as path from 'node:path';
4
4
  import { toFileDirURL, toFileURL } from '@cspell/url';
5
5
  import { createTextDocument, DocumentValidator, extractImportErrors, getDictionary, refreshDictionaryCache, } from 'cspell-lib';
6
6
  import { getDefaultLogger } from '../common/logger.cjs';
7
+ import { defaultOptions } from '../common/options.cjs';
8
+ const ALLOWED_ISSUES_FOR_FLAGGED = new Set(['Forbidden']);
9
+ const ALLOWED_ISSUES_FOR_TYPOS = new Set([...ALLOWED_ISSUES_FOR_FLAGGED, 'Misspelled']);
7
10
  const defaultSettings = {
8
11
  name: 'eslint-configuration-file',
9
12
  patterns: [
@@ -23,6 +26,7 @@ export async function spellCheck(filename, text, ranges, options) {
23
26
  const debugMode = forceLogging || options.debugMode || false;
24
27
  logger.enabled = forceLogging || (options.debugMode ?? (logger.enabled || isDebugModeExtended));
25
28
  const log = logger.log;
29
+ const filterIssues = generateReportingPredicate(options.report);
26
30
  log('options: %o', options);
27
31
  const validator = getDocValidator(filename, text, options);
28
32
  await validator.prepare();
@@ -33,7 +37,8 @@ export async function spellCheck(filename, text, ranges, options) {
33
37
  .map((range, idx) => {
34
38
  const issues = validator
35
39
  .checkText(range, undefined, undefined)
36
- .map((issue) => normalizeIssue(issue, range, idx));
40
+ .map((issue) => normalizeIssue(issue, range, idx))
41
+ .filter(filterIssues);
37
42
  return issues.length ? issues : undefined;
38
43
  })
39
44
  .filter((issues) => !!issues)
@@ -46,14 +51,32 @@ export async function spellCheck(filename, text, ranges, options) {
46
51
  return found;
47
52
  }
48
53
  function normalizeIssue(issue, range, rangeIdx) {
49
- const word = issue.text;
50
- const start = issue.offset;
54
+ const { text: word, offset: start, suggestionsEx: suggestions } = issue;
51
55
  const end = issue.offset + (issue.length || issue.text.length);
52
- const suggestions = issue.suggestionsEx;
53
- const severity = issue.isFlagged ? 'Forbidden' : 'Unknown';
54
- return { word, start, end, suggestions, severity, range, rangeIdx };
56
+ let severity = 'Unknown';
57
+ severity = issue.hasPreferredSuggestions ? 'Misspelled' : severity;
58
+ severity = issue.isFlagged ? 'Forbidden' : severity;
59
+ const hasPreferredFixes = issue.hasPreferredSuggestions || false;
60
+ const hasSimpleSuggestions = issue.hasSimpleSuggestions || false;
61
+ return { word, start, end, suggestions, severity, range, rangeIdx, hasPreferredFixes, hasSimpleSuggestions };
55
62
  }
56
63
  }
64
+ function generateReportingPredicate(report) {
65
+ switch (report) {
66
+ case 'simple': {
67
+ return (issue) => ALLOWED_ISSUES_FOR_TYPOS.has(issue.severity) ||
68
+ (issue.severity === 'Unknown' && issue.hasSimpleSuggestions);
69
+ }
70
+ case 'flagged': {
71
+ return (issue) => ALLOWED_ISSUES_FOR_FLAGGED.has(issue.severity);
72
+ }
73
+ case 'typos': {
74
+ return (issue) => ALLOWED_ISSUES_FOR_TYPOS.has(issue.severity);
75
+ }
76
+ }
77
+ // report === 'all' or undefined
78
+ return () => true;
79
+ }
57
80
  const cache = { lastDoc: undefined };
58
81
  const docValCache = new WeakMap();
59
82
  function getDocValidator(filename, text, options) {
@@ -66,10 +89,24 @@ function getDocValidator(filename, text, options) {
66
89
  return cachedValidator;
67
90
  }
68
91
  const resolveImportsRelativeTo = toFileURL(options.cspellOptionsRoot || import.meta.url, toFileDirURL(options.cwd));
69
- const validator = new DocumentValidator(doc, { ...options, resolveImportsRelativeTo }, settings);
92
+ const report = options.report || 'all';
93
+ const generateSuggestions = report && report !== 'all'
94
+ ? (options.generateSuggestions ?? false)
95
+ : (options.generateSuggestions ?? defaultOptions.generateSuggestions);
96
+ const validator = new DocumentValidator(doc, { ...options, resolveImportsRelativeTo, generateSuggestions }, settings);
70
97
  docValCache.set(doc, validator);
71
98
  return validator;
72
99
  }
100
+ export const mapReportToUnknownWordChoices = {
101
+ all: 'report-all',
102
+ simple: 'report-simple',
103
+ typos: 'report-common-typos',
104
+ flagged: 'report-flagged',
105
+ };
106
+ function mapReportToUnknownWords(report) {
107
+ const unknownWords = report ? mapReportToUnknownWordChoices[report] : undefined;
108
+ return unknownWords ? { unknownWords } : {};
109
+ }
73
110
  function calcInitialSettings(options) {
74
111
  const { customWordListFile, cspell, cwd } = options;
75
112
  const settings = {
@@ -78,6 +115,7 @@ function calcInitialSettings(options) {
78
115
  words: cspell?.words || [],
79
116
  ignoreWords: cspell?.ignoreWords || [],
80
117
  flagWords: cspell?.flagWords || [],
118
+ ...mapReportToUnknownWords(options.report),
81
119
  };
82
120
  if (options.configFile) {
83
121
  const optionCspellImport = options.cspell?.import;
@@ -175,11 +175,25 @@ function spellCheckAST(filename, text, root, options) {
175
175
  return node.parent?.type === 'MemberExpression';
176
176
  }
177
177
  function reportIssue(issue) {
178
- const { word, start, end, severity } = issue;
178
+ const { word, start, end, severity, hasPreferredFixes, hasSimpleSuggestions } = issue;
179
179
  const node = toBeChecked[issue.rangeIdx].node;
180
180
  const nodeType = node.type;
181
181
  const suggestions = normalizeSuggestions(issue.suggestions, nodeType);
182
- return { word, start, end, nodeType, node, suggestions, severity };
182
+ const preferredFixes = suggestions
183
+ ?.filter((sug) => sug.isPreferred)
184
+ .map((sug) => sug.wordAdjustedToMatchCase || sug.word);
185
+ return {
186
+ word,
187
+ start,
188
+ end,
189
+ nodeType,
190
+ node,
191
+ suggestions,
192
+ hasPreferredFixes,
193
+ hasSimpleSuggestions,
194
+ preferredFixes,
195
+ severity,
196
+ };
183
197
  }
184
198
  const processors = {
185
199
  Line: checkComment,
@@ -18,8 +18,13 @@ export interface Issue {
18
18
  start: number;
19
19
  end: number;
20
20
  word: string;
21
- severity: 'Forbidden' | 'Unknown' | 'Hint';
21
+ severity: 'Forbidden' | 'Misspelled' | 'Unknown' | 'Hint';
22
+ /** Possible suggestions. */
22
23
  suggestions: Suggestions;
24
+ /** Indicates that there preferred suggestions. */
25
+ hasPreferredFixes: boolean;
26
+ hasSimpleSuggestions: boolean;
27
+ preferredFixes: string[] | undefined;
23
28
  nodeType: NodeType;
24
29
  node: ASTNode | undefined;
25
30
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "provenance": true
6
6
  },
7
- "version": "9.4.0",
7
+ "version": "9.6.0",
8
8
  "description": "CSpell ESLint plugin",
9
9
  "keywords": [
10
10
  "cspell",
@@ -79,35 +79,35 @@
79
79
  },
80
80
  "devDependencies": {
81
81
  "@eslint/eslintrc": "^3.3.3",
82
- "@eslint/js": "^9.39.1",
82
+ "@eslint/js": "^9.39.2",
83
83
  "@internal/cspell-eslint-plugin-scripts": "",
84
84
  "@types/estree": "^1.0.8",
85
85
  "@types/mocha": "^10.0.10",
86
- "@typescript-eslint/parser": "^8.48.0",
87
- "@typescript-eslint/types": "^8.48.0",
88
- "eslint": "^9.39.1",
86
+ "@typescript-eslint/parser": "^8.52.0",
87
+ "@typescript-eslint/types": "^8.52.0",
88
+ "eslint": "^9.39.2",
89
89
  "eslint-plugin-jsonc": "^2.21.0",
90
90
  "eslint-plugin-mdx": "^3.6.2",
91
91
  "eslint-plugin-n": "^17.23.1",
92
92
  "eslint-plugin-react": "^7.37.5",
93
93
  "eslint-plugin-simple-import-sort": "^12.1.1",
94
- "eslint-plugin-yml": "^1.19.0",
95
- "globals": "^16.5.0",
96
- "jsonc-eslint-parser": "^2.4.1",
94
+ "eslint-plugin-yml": "^1.19.1",
95
+ "globals": "^17.0.0",
96
+ "jsonc-eslint-parser": "^2.4.2",
97
97
  "mocha": "^11.7.5",
98
98
  "ts-json-schema-generator": "^2.4.0",
99
99
  "typescript": "~5.9.3",
100
- "typescript-eslint": "^8.48.0",
101
- "yaml-eslint-parser": "^1.3.1"
100
+ "typescript-eslint": "^8.52.0",
101
+ "yaml-eslint-parser": "^1.3.2"
102
102
  },
103
103
  "dependencies": {
104
- "@cspell/cspell-types": "9.4.0",
105
- "@cspell/url": "9.4.0",
106
- "cspell-lib": "9.4.0",
104
+ "@cspell/cspell-types": "9.6.0",
105
+ "@cspell/url": "9.6.0",
106
+ "cspell-lib": "9.6.0",
107
107
  "synckit": "^0.11.11"
108
108
  },
109
109
  "peerDependencies": {
110
110
  "eslint": "^7 || ^8 || ^9"
111
111
  },
112
- "gitHead": "12dba3d8b880384d1401c765cb2186647f5a266f"
112
+ "gitHead": "163793ddf2a0ad90bc7c90351698a106003297af"
113
113
  }