@cspell/eslint-plugin 5.19.0 → 5.19.3

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
@@ -1,10 +1,16 @@
1
- # [WIP] CSpell ESLint Plugin
1
+ # CSpell ESLint Plugin
2
2
 
3
3
  A spell checker plugin for ESLint based upon CSpell.
4
4
 
5
- ## [WIP] - Work In Progress
5
+ ## Feedback Welcome
6
6
 
7
- This plugin is still in active development. Due to the nature of how files are parsed, the `cspell` command line tool and this ESLint plugin will give different results. It is recommended that ESLint or `cspell` checks a file, but not both. Use `ignorePaths` setting in `cspell.json` to tell the `cspell` command line tool to ignore files checked by ESLint.
7
+ This plugin is still in active development as part of the CSpell suite of tools and applications.
8
+
9
+ ## In Combination with CSpell
10
+
11
+ Due to the nature of how files are parsed, the `cspell` command line tool and this ESLint plugin will give different results.
12
+ It is recommended that either ESLint or `cspell` checks a file, but not both. Use `ignorePaths` setting in `cspell.json` to
13
+ tell the `cspell` command line tool to ignore files checked by ESLint.
8
14
 
9
15
  ## Quick Setup
10
16
 
@@ -21,30 +27,36 @@ This plugin is still in active development. Due to the nature of how files are p
21
27
 
22
28
  ## Options
23
29
 
24
- ```ts
30
+ ````ts
25
31
  interface Options {
26
32
  /**
27
33
  * Number of spelling suggestions to make.
28
34
  * @default 8
29
35
  */
30
36
  numSuggestions: number;
31
-
32
37
  /**
33
38
  * Generate suggestions
34
39
  * @default true
35
40
  */
36
41
  generateSuggestions: boolean;
37
-
38
- /**
39
- * Output debug logs
40
- * @default false
41
- */
42
- debugMode?: boolean;
43
42
  /**
44
43
  * Ignore import and require names
45
44
  * @default true
46
45
  */
47
46
  ignoreImports?: boolean;
47
+ /**
48
+ * Ignore the properties of imported variables, structures, and types.
49
+ *
50
+ * Example:
51
+ * ```
52
+ * import { example } from 'third-party';
53
+ *
54
+ * const msg = example.property; // `property` is not spell checked.
55
+ * ```
56
+ *
57
+ * @default true
58
+ */
59
+ ignoreImportProperties?: boolean;
48
60
  /**
49
61
  * Spell check identifiers (variables names, function names, and class names)
50
62
  * @default true
@@ -65,8 +77,13 @@ interface Options {
65
77
  * @default true
66
78
  */
67
79
  checkComments?: boolean;
80
+ /**
81
+ * Output debug logs
82
+ * @default false
83
+ */
84
+ debugMode?: boolean;
68
85
  }
69
- ```
86
+ ````
70
87
 
71
88
  Example:
72
89
 
package/dist/index.d.ts CHANGED
@@ -45,7 +45,20 @@ interface Check {
45
45
  */
46
46
  ignoreImports?: boolean;
47
47
  /**
48
- * Spell check identifiers (variables names, function names, and class names)
48
+ * Ignore the properties of imported variables, structures, and types.
49
+ *
50
+ * Example:
51
+ * ```
52
+ * import { example } from 'third-party';
53
+ *
54
+ * const msg = example.property; // `property` is not spell checked.
55
+ * ```
56
+ *
57
+ * @default true
58
+ */
59
+ ignoreImportProperties?: boolean;
60
+ /**
61
+ * Spell check identifiers (variables names, function names, class names, etc.)
49
62
  * @default true
50
63
  */
51
64
  checkIdentifiers?: boolean;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @cspell/eslint-plugin v5.18.5
2
+ * @cspell/eslint-plugin v5.19.2
3
3
  * Copyright 2022 Jason Dent <jason@streetsidesoftware.nl>
4
4
  * Released under the MIT License
5
5
  * https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-eslint-plugin#readme
@@ -23,6 +23,7 @@ const defaultCheckOptions = {
23
23
  checkStrings: true,
24
24
  checkStringTemplates: true,
25
25
  ignoreImports: true,
26
+ ignoreImportProperties: true,
26
27
  };
27
28
  const defaultOptions = {
28
29
  ...defaultCheckOptions,
@@ -47,7 +48,7 @@ var properties = {
47
48
  },
48
49
  checkIdentifiers: {
49
50
  "default": true,
50
- description: "Spell check identifiers (variables names, function names, and class names)",
51
+ description: "Spell check identifiers (variables names, function names, class names, etc.)",
51
52
  type: "boolean"
52
53
  },
53
54
  checkStringTemplates: {
@@ -70,6 +71,11 @@ var properties = {
70
71
  description: "Generate suggestions",
71
72
  type: "boolean"
72
73
  },
74
+ ignoreImportProperties: {
75
+ "default": true,
76
+ description: "Ignore the properties of imported variables, structures, and types.\n\nExample: ``` import { example } from 'third-party';\n\nconst msg = example.property; // `property` is not spell checked. ```",
77
+ type: "boolean"
78
+ },
73
79
  ignoreImports: {
74
80
  "default": true,
75
81
  description: "Ignore import and require names",
@@ -129,19 +135,21 @@ function log(...args) {
129
135
  }
130
136
  function create(context) {
131
137
  const options = normalizeOptions(context.options[0]);
138
+ const toIgnore = new Set();
132
139
  const importedIdentifiers = new Set();
133
140
  isDebugMode = options.debugMode || false;
134
141
  isDebugMode && logContext(context);
135
- const doc = cspellLib.createTextDocument({ uri: context.getFilename(), content: context.getSourceCode().getText() });
136
- const validator = new cspellLib.DocumentValidator(doc, options, defaultSettings);
142
+ const validator = getDocValidator(context);
137
143
  validator.prepareSync();
138
144
  function checkLiteral(node) {
139
145
  if (!options.checkStrings)
140
146
  return;
141
147
  if (typeof node.value === 'string') {
148
+ debugNode(node, node.value);
142
149
  if (options.ignoreImports && isImportOrRequired(node))
143
150
  return;
144
- debugNode(node, node.value);
151
+ if (options.ignoreImportProperties && isImportedProperty(node))
152
+ return;
145
153
  checkNodeText(node, node.value);
146
154
  }
147
155
  }
@@ -153,15 +161,29 @@ function create(context) {
153
161
  checkNodeText(node, node.value.cooked || node.value.raw);
154
162
  }
155
163
  function checkIdentifier(node) {
156
- if (options.ignoreImports && isImportIdentifier(node)) {
157
- importedIdentifiers.add(node.name);
158
- return;
164
+ debugNode(node, node.name);
165
+ if (options.ignoreImports) {
166
+ if (isRawImportIdentifier(node)) {
167
+ toIgnore.add(node.name);
168
+ return;
169
+ }
170
+ if (isImportIdentifier(node)) {
171
+ importedIdentifiers.add(node.name);
172
+ if (isLocalImportIdentifierUnique(node)) {
173
+ checkNodeText(node, node.name);
174
+ }
175
+ return;
176
+ }
177
+ else if (options.ignoreImportProperties && isImportedProperty(node)) {
178
+ return;
179
+ }
159
180
  }
160
181
  if (!options.checkIdentifiers)
161
182
  return;
162
- if (importedIdentifiers.has(node.name))
183
+ if (toIgnore.has(node.name) && !isObjectProperty(node))
184
+ return;
185
+ if (skipCheckForRawImportIdentifiers(node))
163
186
  return;
164
- debugNode(node, node.name);
165
187
  checkNodeText(node, node.name);
166
188
  }
167
189
  function checkComment(node) {
@@ -184,12 +206,49 @@ function create(context) {
184
206
  return [];
185
207
  }
186
208
  function isImportIdentifier(node) {
209
+ const parent = node.parent;
210
+ if (node.type !== 'Identifier' || !parent)
211
+ return false;
212
+ return ((parent.type === 'ImportSpecifier' ||
213
+ parent.type === 'ImportNamespaceSpecifier' ||
214
+ parent.type === 'ImportDefaultSpecifier') &&
215
+ parent.local === node);
216
+ }
217
+ function isRawImportIdentifier(node) {
187
218
  const parent = node.parent;
188
219
  if (node.type !== 'Identifier' || !parent)
189
220
  return false;
190
221
  return ((parent.type === 'ImportSpecifier' && parent.imported === node) ||
191
222
  (parent.type === 'ExportSpecifier' && parent.local === node));
192
223
  }
224
+ function isLocalImportIdentifierUnique(node) {
225
+ var _a, _b, _c, _d;
226
+ const parent = getImportParent(node);
227
+ if (!parent)
228
+ return true;
229
+ const { imported, local } = parent;
230
+ if (imported.name !== local.name)
231
+ return true;
232
+ return ((_a = imported.range) === null || _a === void 0 ? void 0 : _a[0]) !== ((_b = local.range) === null || _b === void 0 ? void 0 : _b[0]) && ((_c = imported.range) === null || _c === void 0 ? void 0 : _c[1]) !== ((_d = local.range) === null || _d === void 0 ? void 0 : _d[1]);
233
+ }
234
+ function getImportParent(node) {
235
+ const parent = node.parent;
236
+ return (parent === null || parent === void 0 ? void 0 : parent.type) === 'ImportSpecifier' ? parent : undefined;
237
+ }
238
+ function skipCheckForRawImportIdentifiers(node) {
239
+ if (options.ignoreImports)
240
+ return false;
241
+ const parent = getImportParent(node);
242
+ return !!parent && parent.imported === node && !isLocalImportIdentifierUnique(node);
243
+ }
244
+ function isImportedProperty(node) {
245
+ const obj = findOriginObject(node);
246
+ return !!obj && obj.type === 'Identifier' && importedIdentifiers.has(obj.name);
247
+ }
248
+ function isObjectProperty(node) {
249
+ var _a;
250
+ return ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === 'MemberExpression';
251
+ }
193
252
  function reportIssue(issue) {
194
253
  var _a;
195
254
  const messageId = issue.isFlagged ? 'wordForbidden' : 'wordUnknown';
@@ -295,6 +354,19 @@ function create(context) {
295
354
  function inheritanceSummary(node) {
296
355
  return inheritance(node).join(' ');
297
356
  }
357
+ /**
358
+ * find the origin of a member expression
359
+ */
360
+ function findOriginObject(node) {
361
+ const parent = node.parent;
362
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) !== 'MemberExpression' || parent.property !== node)
363
+ return undefined;
364
+ let obj = parent.object;
365
+ while (obj.type === 'MemberExpression') {
366
+ obj = obj.object;
367
+ }
368
+ return obj;
369
+ }
298
370
  function isFunctionCall(node, name) {
299
371
  return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === name;
300
372
  }
@@ -357,6 +429,33 @@ const configs = {
357
429
  },
358
430
  },
359
431
  };
432
+ const cache = { lastDoc: undefined };
433
+ const docValCache = new WeakMap();
434
+ function getDocValidator(context) {
435
+ const text = context.getSourceCode().getText();
436
+ const doc = getTextDocument(context.getFilename(), text);
437
+ const cachedValidator = docValCache.get(doc);
438
+ if (cachedValidator) {
439
+ cachedValidator.updateDocumentText(text);
440
+ return cachedValidator;
441
+ }
442
+ const options = normalizeOptions(context.options[0]);
443
+ isDebugMode = options.debugMode || false;
444
+ isDebugMode && logContext(context);
445
+ const validator = new cspellLib.DocumentValidator(doc, options, defaultSettings);
446
+ docValCache.set(doc, validator);
447
+ return validator;
448
+ }
449
+ function getTextDocument(filename, content) {
450
+ var _a;
451
+ if (((_a = cache.lastDoc) === null || _a === void 0 ? void 0 : _a.filename) === filename) {
452
+ return cache.lastDoc.doc;
453
+ }
454
+ const doc = cspellLib.createTextDocument({ uri: filename, content });
455
+ // console.error(`CreateTextDocument: ${doc.uri}`);
456
+ cache.lastDoc = { filename, doc };
457
+ return doc;
458
+ }
360
459
 
361
460
  exports.configs = configs;
362
461
  exports.rules = rules;
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  /*!
2
- * @cspell/eslint-plugin v5.18.5
2
+ * @cspell/eslint-plugin v5.19.2
3
3
  * Copyright 2022 Jason Dent <jason@streetsidesoftware.nl>
4
4
  * Released under the MIT License
5
5
  * https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-eslint-plugin#readme
6
6
  */
7
7
 
8
8
  import assert from 'assert';
9
- import { createTextDocument, DocumentValidator } from 'cspell-lib';
9
+ import { DocumentValidator, createTextDocument } from 'cspell-lib';
10
10
  import { format } from 'util';
11
11
 
12
12
  const defaultCheckOptions = {
@@ -15,6 +15,7 @@ const defaultCheckOptions = {
15
15
  checkStrings: true,
16
16
  checkStringTemplates: true,
17
17
  ignoreImports: true,
18
+ ignoreImportProperties: true,
18
19
  };
19
20
  const defaultOptions = {
20
21
  ...defaultCheckOptions,
@@ -39,7 +40,7 @@ var properties = {
39
40
  },
40
41
  checkIdentifiers: {
41
42
  "default": true,
42
- description: "Spell check identifiers (variables names, function names, and class names)",
43
+ description: "Spell check identifiers (variables names, function names, class names, etc.)",
43
44
  type: "boolean"
44
45
  },
45
46
  checkStringTemplates: {
@@ -62,6 +63,11 @@ var properties = {
62
63
  description: "Generate suggestions",
63
64
  type: "boolean"
64
65
  },
66
+ ignoreImportProperties: {
67
+ "default": true,
68
+ description: "Ignore the properties of imported variables, structures, and types.\n\nExample: ``` import { example } from 'third-party';\n\nconst msg = example.property; // `property` is not spell checked. ```",
69
+ type: "boolean"
70
+ },
65
71
  ignoreImports: {
66
72
  "default": true,
67
73
  description: "Ignore import and require names",
@@ -121,19 +127,21 @@ function log(...args) {
121
127
  }
122
128
  function create(context) {
123
129
  const options = normalizeOptions(context.options[0]);
130
+ const toIgnore = new Set();
124
131
  const importedIdentifiers = new Set();
125
132
  isDebugMode = options.debugMode || false;
126
133
  isDebugMode && logContext(context);
127
- const doc = createTextDocument({ uri: context.getFilename(), content: context.getSourceCode().getText() });
128
- const validator = new DocumentValidator(doc, options, defaultSettings);
134
+ const validator = getDocValidator(context);
129
135
  validator.prepareSync();
130
136
  function checkLiteral(node) {
131
137
  if (!options.checkStrings)
132
138
  return;
133
139
  if (typeof node.value === 'string') {
140
+ debugNode(node, node.value);
134
141
  if (options.ignoreImports && isImportOrRequired(node))
135
142
  return;
136
- debugNode(node, node.value);
143
+ if (options.ignoreImportProperties && isImportedProperty(node))
144
+ return;
137
145
  checkNodeText(node, node.value);
138
146
  }
139
147
  }
@@ -145,15 +153,29 @@ function create(context) {
145
153
  checkNodeText(node, node.value.cooked || node.value.raw);
146
154
  }
147
155
  function checkIdentifier(node) {
148
- if (options.ignoreImports && isImportIdentifier(node)) {
149
- importedIdentifiers.add(node.name);
150
- return;
156
+ debugNode(node, node.name);
157
+ if (options.ignoreImports) {
158
+ if (isRawImportIdentifier(node)) {
159
+ toIgnore.add(node.name);
160
+ return;
161
+ }
162
+ if (isImportIdentifier(node)) {
163
+ importedIdentifiers.add(node.name);
164
+ if (isLocalImportIdentifierUnique(node)) {
165
+ checkNodeText(node, node.name);
166
+ }
167
+ return;
168
+ }
169
+ else if (options.ignoreImportProperties && isImportedProperty(node)) {
170
+ return;
171
+ }
151
172
  }
152
173
  if (!options.checkIdentifiers)
153
174
  return;
154
- if (importedIdentifiers.has(node.name))
175
+ if (toIgnore.has(node.name) && !isObjectProperty(node))
176
+ return;
177
+ if (skipCheckForRawImportIdentifiers(node))
155
178
  return;
156
- debugNode(node, node.name);
157
179
  checkNodeText(node, node.name);
158
180
  }
159
181
  function checkComment(node) {
@@ -176,12 +198,49 @@ function create(context) {
176
198
  return [];
177
199
  }
178
200
  function isImportIdentifier(node) {
201
+ const parent = node.parent;
202
+ if (node.type !== 'Identifier' || !parent)
203
+ return false;
204
+ return ((parent.type === 'ImportSpecifier' ||
205
+ parent.type === 'ImportNamespaceSpecifier' ||
206
+ parent.type === 'ImportDefaultSpecifier') &&
207
+ parent.local === node);
208
+ }
209
+ function isRawImportIdentifier(node) {
179
210
  const parent = node.parent;
180
211
  if (node.type !== 'Identifier' || !parent)
181
212
  return false;
182
213
  return ((parent.type === 'ImportSpecifier' && parent.imported === node) ||
183
214
  (parent.type === 'ExportSpecifier' && parent.local === node));
184
215
  }
216
+ function isLocalImportIdentifierUnique(node) {
217
+ var _a, _b, _c, _d;
218
+ const parent = getImportParent(node);
219
+ if (!parent)
220
+ return true;
221
+ const { imported, local } = parent;
222
+ if (imported.name !== local.name)
223
+ return true;
224
+ return ((_a = imported.range) === null || _a === void 0 ? void 0 : _a[0]) !== ((_b = local.range) === null || _b === void 0 ? void 0 : _b[0]) && ((_c = imported.range) === null || _c === void 0 ? void 0 : _c[1]) !== ((_d = local.range) === null || _d === void 0 ? void 0 : _d[1]);
225
+ }
226
+ function getImportParent(node) {
227
+ const parent = node.parent;
228
+ return (parent === null || parent === void 0 ? void 0 : parent.type) === 'ImportSpecifier' ? parent : undefined;
229
+ }
230
+ function skipCheckForRawImportIdentifiers(node) {
231
+ if (options.ignoreImports)
232
+ return false;
233
+ const parent = getImportParent(node);
234
+ return !!parent && parent.imported === node && !isLocalImportIdentifierUnique(node);
235
+ }
236
+ function isImportedProperty(node) {
237
+ const obj = findOriginObject(node);
238
+ return !!obj && obj.type === 'Identifier' && importedIdentifiers.has(obj.name);
239
+ }
240
+ function isObjectProperty(node) {
241
+ var _a;
242
+ return ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === 'MemberExpression';
243
+ }
185
244
  function reportIssue(issue) {
186
245
  var _a;
187
246
  const messageId = issue.isFlagged ? 'wordForbidden' : 'wordUnknown';
@@ -287,6 +346,19 @@ function create(context) {
287
346
  function inheritanceSummary(node) {
288
347
  return inheritance(node).join(' ');
289
348
  }
349
+ /**
350
+ * find the origin of a member expression
351
+ */
352
+ function findOriginObject(node) {
353
+ const parent = node.parent;
354
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) !== 'MemberExpression' || parent.property !== node)
355
+ return undefined;
356
+ let obj = parent.object;
357
+ while (obj.type === 'MemberExpression') {
358
+ obj = obj.object;
359
+ }
360
+ return obj;
361
+ }
290
362
  function isFunctionCall(node, name) {
291
363
  return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === name;
292
364
  }
@@ -349,6 +421,33 @@ const configs = {
349
421
  },
350
422
  },
351
423
  };
424
+ const cache = { lastDoc: undefined };
425
+ const docValCache = new WeakMap();
426
+ function getDocValidator(context) {
427
+ const text = context.getSourceCode().getText();
428
+ const doc = getTextDocument(context.getFilename(), text);
429
+ const cachedValidator = docValCache.get(doc);
430
+ if (cachedValidator) {
431
+ cachedValidator.updateDocumentText(text);
432
+ return cachedValidator;
433
+ }
434
+ const options = normalizeOptions(context.options[0]);
435
+ isDebugMode = options.debugMode || false;
436
+ isDebugMode && logContext(context);
437
+ const validator = new DocumentValidator(doc, options, defaultSettings);
438
+ docValCache.set(doc, validator);
439
+ return validator;
440
+ }
441
+ function getTextDocument(filename, content) {
442
+ var _a;
443
+ if (((_a = cache.lastDoc) === null || _a === void 0 ? void 0 : _a.filename) === filename) {
444
+ return cache.lastDoc.doc;
445
+ }
446
+ const doc = createTextDocument({ uri: filename, content });
447
+ // console.error(`CreateTextDocument: ${doc.uri}`);
448
+ cache.lastDoc = { filename, doc };
449
+ return doc;
450
+ }
352
451
 
353
452
  export { configs, rules };
354
453
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "5.19.0",
6
+ "version": "5.19.3",
7
7
  "description": "[WIP] CSpell ESLint plugin",
8
8
  "keywords": [
9
9
  "cspell",
@@ -60,19 +60,19 @@
60
60
  "@rollup/plugin-typescript": "^8.3.1",
61
61
  "@types/eslint": "^8.4.1",
62
62
  "@types/estree": "^0.0.51",
63
- "@types/node": "^17.0.21",
64
- "@typescript-eslint/parser": "^5.14.0",
65
- "@typescript-eslint/types": "^5.14.0",
66
- "@typescript-eslint/typescript-estree": "^5.14.0",
63
+ "@types/node": "^17.0.22",
64
+ "@typescript-eslint/parser": "^5.16.0",
65
+ "@typescript-eslint/types": "^5.16.0",
66
+ "@typescript-eslint/typescript-estree": "^5.16.0",
67
67
  "eslint": "^8.11.0",
68
68
  "mocha": "^9.2.2",
69
69
  "rimraf": "^3.0.2",
70
- "rollup": "^2.70.0",
70
+ "rollup": "^2.70.1",
71
71
  "rollup-plugin-dts": "^4.2.0",
72
- "ts-json-schema-generator": "^0.98.0"
72
+ "ts-json-schema-generator": "^1.0.0"
73
73
  },
74
74
  "dependencies": {
75
- "cspell-lib": "^5.19.0"
75
+ "cspell-lib": "^5.19.3"
76
76
  },
77
- "gitHead": "7873efe40a3287d3e025f705e1712c12d3cb392e"
77
+ "gitHead": "df43735d8f85fddbbd0befca7d98956f50474ce7"
78
78
  }