@josundt/eslint-config 4.4.1 → 4.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josundt/eslint-config",
3
- "version": "4.4.1",
3
+ "version": "4.8.3",
4
4
  "description": "ESLint ruleset with required plugins for josundt TypeScript projects",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -23,19 +23,21 @@
23
23
  "license": "ISC",
24
24
  "files": [
25
25
  "*.js",
26
- "rules/**/*.js"
26
+ "rules/**/*.js",
27
+ "utils/**/*.js"
27
28
  ],
28
29
  "peerDependencies": {
29
- "typescript": ">=4.4.2"
30
+ "typescript": ">=4.8.2"
30
31
  },
31
32
  "dependencies": {
32
- "@typescript-eslint/eslint-plugin": "4.30.0",
33
- "@typescript-eslint/parser": "4.30.0",
34
- "eslint": "7.32.0",
35
- "eslint-import-resolver-typescript": "2.4.0",
36
- "eslint-plugin-deprecation": "1.2.1",
37
- "eslint-plugin-import": "2.24.2",
38
- "eslint-plugin-jasmine": "4.1.2",
39
- "eslint-plugin-jsdoc": "36.0.8",
40
- "eslint-plugin-unicorn": "35.0.0"
41
- }}
33
+ "@typescript-eslint/eslint-plugin": "5.36.1",
34
+ "@typescript-eslint/parser": "5.36.1",
35
+ "eslint": "8.23.0",
36
+ "eslint-import-resolver-typescript": "3.5.0",
37
+ "eslint-plugin-deprecation": "1.3.2",
38
+ "eslint-plugin-import": "2.26.0",
39
+ "eslint-plugin-jasmine": "4.1.3",
40
+ "eslint-plugin-jsdoc": "39.3.6",
41
+ "eslint-plugin-unicorn": "43.0.2"
42
+ }
43
+ }
package/rules/eslint.js CHANGED
@@ -61,7 +61,7 @@ module.exports = {
61
61
  ],
62
62
  "guard-for-in": "error",
63
63
  "handle-callback-err": "error",
64
- "id-blacklist": [
64
+ "id-denylist": [
65
65
  "error",
66
66
  "any",
67
67
  "Number",
@@ -86,7 +86,14 @@ module.exports = {
86
86
  "FunctionExpression": {
87
87
  "parameters": "first"
88
88
  },
89
- "SwitchCase": 1
89
+ "SwitchCase": 1,
90
+
91
+ // Fix to decorator indentation problem
92
+ "ignoredNodes": [
93
+ "FunctionExpression > .params[decorators.length > 0]",
94
+ "FunctionExpression > .params > :matches(Decorator, :not(:first-child))",
95
+ "ClassBody.body > PropertyDefinition[decorators.length > 0] > .key"
96
+ ]
90
97
  }
91
98
  ],
92
99
  "init-declarations": "off",
@@ -134,7 +141,9 @@ module.exports = {
134
141
  "no-duplicate-imports": "error",
135
142
  "no-empty": "error",
136
143
  "no-empty-character-class": "error",
137
- "no-empty-function": "error",
144
+ "no-empty-function": ["error", {
145
+ //allow: [/*"functions", "arrowFunctions", "generatorFunctions", "methods", "generatorMethods", "getters", "setters", "constructors", "asyncFunctions", "asyncMethods"*/]
146
+ }],
138
147
  "no-eval": "error",
139
148
  "no-ex-assign": "error",
140
149
  "no-extra-bind": "error",
@@ -198,7 +207,10 @@ module.exports = {
198
207
  "no-shadow": [
199
208
  "error",
200
209
  {
201
- "hoist": "all"
210
+ "hoist": "all",
211
+ "builtinGlobals": false,
212
+ "ignoreOnInitialization": false,
213
+ "allow": [] // array of identifier names for which shadowing is allowed
202
214
  }
203
215
  ],
204
216
  "no-sparse-arrays": "error",
@@ -212,6 +224,12 @@ module.exports = {
212
224
  "allowAfterThis": true
213
225
  }
214
226
  ],
227
+ "no-unneeded-ternary": [
228
+ "error",
229
+ {
230
+ "defaultAssignment": true
231
+ }
232
+ ],
215
233
  "no-unsafe-finally": "error",
216
234
  "no-unused-expressions": "error", // Switched off since it breaks support for null coalescing and optional chaining in TypeScript: https://github.com/typescript-eslint/typescript-eslint/issues/1051 - https://github.com/typescript-eslint/typescript-eslint/issues/1052
217
235
  "no-unused-labels": "error",
@@ -219,7 +237,8 @@ module.exports = {
219
237
  "error",
220
238
  {
221
239
  "vars": "all",
222
- "args": "none"
240
+ "args": "none",
241
+ "destructuredArrayIgnorePattern": "^_"
223
242
  }
224
243
  ],
225
244
  "no-use-before-define": "error",
@@ -236,12 +255,17 @@ module.exports = {
236
255
  "never"
237
256
  ],
238
257
  "padding-line-between-statements": [
239
- "off",
240
- {
241
- "blankLine": "always",
242
- "prev": "*",
243
- "next": "return"
244
- }
258
+ "error",
259
+ { "blankLine": "always", "prev": ["directive", "import"], "next": "*" },
260
+ { "blankLine": "any", "prev": ["directive", "import"], "next": ["directive", "import"] },
261
+
262
+ { "blankLine": "always", "prev": "*", "next": ["export", "class", "function", "iife"] },
263
+ { "blankLine": "always", "prev": ["export", "class", "function", "iife"], "next": "*" },
264
+
265
+ // { "blankLine": "always", "prev": "*", "next": "return" }, // Newline before return
266
+
267
+ // { "blankLine": "always", "prev": "*", "next": "multiline-block-like" }, // Newline BEFORE multiline block
268
+ // { "blankLine": "always", "prev": "multiline-block-like", "next": "*" } // Newline AFTER multiline block
245
269
  ],
246
270
  "prefer-arrow-callback": "error",
247
271
  "prefer-const": "error",
@@ -262,8 +286,12 @@ module.exports = {
262
286
  "error",
263
287
  "always"
264
288
  ],
289
+ "space-before-blocks": [
290
+ "error",
291
+ "always"
292
+ ],
265
293
  "space-before-function-paren": [
266
- "warn",
294
+ "error",
267
295
  {
268
296
  "anonymous": "always",
269
297
  "asyncArrow": "always",
@@ -8,12 +8,13 @@ module.exports = {
8
8
  "rules": {
9
9
  "import/extensions": [ // Ensure all local .ts file imports use .js extension
10
10
  "error",
11
- "always"
11
+ "ignorePackages"
12
12
  ],
13
13
  "import/no-commonjs": "error", // Disallow require() syntax - only esm syntax allowed
14
14
  "import/no-nodejs-modules": "error", // Disallowed: import * as path from "path"; Allowed: import * as path from "node:path";
15
15
  "import/no-default-export": "off",
16
16
  "import/no-deprecated": "error",
17
+ "import/no-duplicates": "error",
17
18
  "import/no-extraneous-dependencies": "off",
18
19
  "import/no-internal-modules": "off",
19
20
  "import/no-unassigned-import": "error",
@@ -1,9 +1,16 @@
1
- const eslintRuleSet = require("./eslint");
1
+ const eslintRuleSet = require("./eslint.js");
2
+ const { deepMergeObjects } = require("../utils/merge.js");
3
+
2
4
  const eslintRules = eslintRuleSet.rules;
3
5
 
4
6
  // Map of all the typescript-eslint extensions.
5
- // If extension rule has additional properties compared to standard eslint rule,
6
- // the value of the map item has an object that will be merged with standard rule; otherwise null.
7
+ // If extension rule has additional properties compared to standard eslint rule:
8
+ // If the map value is an object:
9
+ // The object will be merged merged with standard rule (rule object 1);
10
+ // If the map value is an array:
11
+ // The items of the array will be added as a new rule options object.
12
+ // If the map value is a function:
13
+ // The standard eslint options object will be passed as parameters, the return statement will be added as options.
7
14
  const extensions = new Map([
8
15
  ["brace-style", null],
9
16
  ["comma-dangle", null],
@@ -23,38 +30,51 @@ const extensions = new Map([
23
30
  }],
24
31
  ["no-array-constructor", null],
25
32
  ["no-dupe-class-members", null],
26
- ["no-duplicate-imports", null],
27
- ["no-empty-function", null],
33
+ ["no-empty-function", {
34
+ allow: ["private-constructors", "protected-constructors" /* "decoratedFunctions", "overrideMethods" */ ]
35
+ }],
28
36
  ["no-extra-parens", null],
29
37
  ["no-extra-semi", null],
30
38
  ["no-invalid-this", null],
31
39
  ["no-loop-func", null],
32
40
  ["no-loss-of-precision", null],
33
41
  ["no-magic-numbers", null],
34
- ["no-shadow", {
35
- "ignoreTypeValueShadow": true,
36
- "ignoreFunctionTypeParameterNameValueShadow": true
42
+ ["no-shadow", v => {
43
+ const o = {
44
+ ...v,
45
+ "ignoreTypeValueShadow": true,
46
+ "ignoreFunctionTypeParameterNameValueShadow": true
47
+ };
48
+ delete o["ignoreOnInitialization"]; // Temporary bug in @typescript/eslint "no-shadow" extension rule - "ignoreOnInitialization" property considered invalid
49
+ return o;
37
50
  }],
38
51
  ["no-unused-expressions", null],
39
52
  ["no-unused-vars", null],
40
53
  ["no-use-before-define", null],
41
54
  ["no-useless-constructor", null],
42
55
  ["object-curly-spacing", null],
56
+ ["padding-line-between-statements", [
57
+ { "blankLine": "always", "prev": "*", "next": ["interface", "type"] },
58
+ { "blankLine": "always", "prev": ["interface", "type"], "next": "*" }
59
+ ]],
43
60
  ["quotes", null],
44
61
  ["require-await", null],
45
62
  ["return-await", null],
46
63
  ["semi", null],
64
+ ["space-before-blocks", null],
47
65
  ["space-before-function-paren", null],
48
- ["space-infix-ops", null]
66
+ // ["space-infix-ops", null] // buggy with typescript (as of 5.27.0) -- switched off below
49
67
  ]);
50
68
 
51
69
  switchOffExtensions = new Set([
52
70
  "no-unused-vars",
53
71
  "no-invalid-this",
54
72
  "no-use-before-define",
55
- "no-useless-constructor"
73
+ "no-useless-constructor",
74
+ "space-infix-ops"
56
75
  ]);
57
76
 
77
+
58
78
  // Building eslint-typescript rules for existsing eslint rules and switching off original eslint rule
59
79
  const extendedEslintRules = Object.entries(eslintRules).reduce((extRules, [key, value]) => {
60
80
 
@@ -73,8 +93,22 @@ const extendedEslintRules = Object.entries(eslintRules).reduce((extRules, [key,
73
93
  } else {
74
94
  // If extension rule has extended options, merge with standard eslint rule options:
75
95
  if (extension !== null) {
76
- value = [...value];
77
- value[value.length - 1] = { ...value[value.length - 1], ...extension }
96
+ // Ensure value is array if only severity string:
97
+ value = Array.isArray(value) ? [...value] : [value];
98
+ if (Array.isArray(extension)) {
99
+ value.push(...extension);
100
+ } else if (typeof extension === "object") {
101
+ // If array only contains severity string, push object
102
+ if (value.length === 1) {
103
+ value.push(extension);
104
+ // Else merge object
105
+ } else {
106
+ value[value.length - 1] = deepMergeObjects(value[value.length - 1], extension);
107
+ }
108
+ } else if (typeof extension === "function") {
109
+ const [, ...options] = value;
110
+ value[value.length - 1] = { ...extension(...options) };
111
+ }
78
112
  }
79
113
  // Add extension rule value with the @typescript-eslint key prefix
80
114
  extRules[`@typescript-eslint/${key}`] = value;
@@ -117,9 +151,20 @@ module.exports = {
117
151
  }
118
152
  ],
119
153
  "@typescript-eslint/await-thenable": "error",
120
- "@typescript-eslint/ban-ts-comment": "error",
154
+ "@typescript-eslint/ban-ts-comment": [
155
+ "error",
156
+ {
157
+ "ts-expect-error": "allow-with-description",
158
+ "ts-ignore": true,
159
+ "ts-nocheck": true,
160
+ "ts-check": false,
161
+ "minimumDescriptionLength": 10,
162
+ //"descriptionFormat": "someformathere"
163
+ }
164
+ ],
121
165
  "@typescript-eslint/ban-tslint-comment": "error", // No longer use tslint - remove rules
122
166
  "@typescript-eslint/ban-types": "off", // Can be used to ban certain types
167
+ "@typescript-eslint/consistent-generic-constructors": ["off", "constructor"],
123
168
  "@typescript-eslint/consistent-indexed-object-style": ["error", "record"],
124
169
  "@typescript-eslint/consistent-type-assertions": [
125
170
  "error",
@@ -132,6 +177,7 @@ module.exports = {
132
177
  "error",
133
178
  "interface"
134
179
  ],
180
+ "@typescript-eslint/consistent-type-exports": "error",
135
181
  "@typescript-eslint/consistent-type-imports": [
136
182
  "off",
137
183
  {
@@ -145,7 +191,8 @@ module.exports = {
145
191
  "allowExpressions": true,
146
192
  "allowTypedFunctionExpressions": true,
147
193
  "allowHigherOrderFunctions": true,
148
- "allowConciseArrowFunctionExpressionsStartingWithVoid": true
194
+ "allowConciseArrowFunctionExpressionsStartingWithVoid": true,
195
+ "allowedNames": []
149
196
  }
150
197
  ],
151
198
  "@typescript-eslint/explicit-member-accessibility": [
@@ -159,9 +206,9 @@ module.exports = {
159
206
  {
160
207
  "allowArgumentsExplicitlyTypedAsAny": false,
161
208
  "allowDirectConstAssertionInArrowFunctions": true,
162
- "allowedNames": [],
163
209
  "allowHigherOrderFunctions": true,
164
210
  "allowTypedFunctionExpressions": true,
211
+ "allowedNames": []
165
212
  }
166
213
  ],
167
214
  "@typescript-eslint/member-delimiter-style": [
@@ -233,6 +280,7 @@ module.exports = {
233
280
  "ignoreArrowShorthand": true
234
281
  }
235
282
  ],
283
+ "@typescript-eslint/no-duplicate-enum-values": "error",
236
284
  "@typescript-eslint/no-dynamic-delete": "error",
237
285
  "@typescript-eslint/no-empty-interface": "off",
238
286
  "@typescript-eslint/no-explicit-any": "off",
@@ -249,20 +297,36 @@ module.exports = {
249
297
  "allowAsThisParameter": true
250
298
  }
251
299
  ],
300
+ "@typescript-eslint/no-meaningless-void-operator": "error",
252
301
  "@typescript-eslint/no-misused-new": "error",
253
- "@typescript-eslint/no-misused-promises": "error",
302
+ "@typescript-eslint/no-misused-promises": [
303
+ "error",
304
+ {
305
+ checksConditionals: true,
306
+ checksSpreads: true,
307
+ checksVoidReturn: true // {
308
+ // arguments: true, //Disables checking an asynchronous function passed as argument where the parameter type expects a function that returns void
309
+ // attributes: true, //Disables checking an asynchronous function passed as a JSX attribute expected to be a function that returns void
310
+ // properties: true, //Disables checking an asynchronous function passed as an object property expected to be a function that returns void
311
+ // returns: true, //Disables checking an asynchronous function returned in a function whose return type is a function that returns void
312
+ // variables: true //Disables checking an asynchronous function used as a variable whose return type is a function that returns void
313
+ // }
314
+ }
315
+ ],
254
316
  "@typescript-eslint/no-namespace": "off",
317
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
255
318
  "@typescript-eslint/no-non-null-asserted-optional-chain": "error",
256
319
  "@typescript-eslint/no-non-null-assertion": "off",
257
- "@typescript-eslint/no-parameter-properties": [
320
+ "@typescript-eslint/no-redundant-type-constituents": "error",
321
+ "@typescript-eslint/no-require-imports": "error",
322
+ "@typescript-eslint/no-this-alias": "error",
323
+ "@typescript-eslint/no-throw-literal": [
258
324
  "error",
259
325
  {
260
- "allows": ["private readonly", "private", "protected readonly"]
326
+ "allowThrowingAny": false, // Default is to allow throwing values of type any
327
+ "allowThrowingUnknown": true // Default is to allow throwing values of type unknown
261
328
  }
262
329
  ],
263
- "@typescript-eslint/no-require-imports": "error",
264
- "@typescript-eslint/no-this-alias": "error",
265
- "@typescript-eslint/no-throw-literal": "error",
266
330
  "@typescript-eslint/no-type-alias": "off",
267
331
  "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
268
332
  "@typescript-eslint/no-unnecessary-condition": "off", // allow runtime null checks etc even if reported not necessary by type system
@@ -276,17 +340,31 @@ module.exports = {
276
340
  "@typescript-eslint/no-unsafe-member-access": "error",
277
341
  "@typescript-eslint/no-unsafe-return": "error",
278
342
  "@typescript-eslint/no-unused-vars-experimental": "off", // to strict with method params...
343
+ "@typescript-eslint/no-useless-empty-export": "error",
279
344
  "@typescript-eslint/no-var-requires": "error",
280
345
  "@typescript-eslint/non-nullable-type-assertion-style": "error",
281
- "@typescript-eslint/prefer-as-const": "error",
346
+ "@typescript-eslint/parameter-properties": [
347
+ "error",
348
+ {
349
+ "prefer": "class-property", // or "parameter-property"
350
+ "allow": ["private readonly", "private", "protected readonly"]
351
+ }
352
+ ],
353
+ "@typescript-eslint/prefer-as-const": "off",
282
354
  "@typescript-eslint/prefer-enum-initializers": "error",
283
355
  "@typescript-eslint/prefer-for-of": "error",
284
356
  "@typescript-eslint/prefer-function-type": "error",
285
357
  "@typescript-eslint/prefer-includes": "error",
286
358
  "@typescript-eslint/prefer-literal-enum-member": "error",
287
359
  "@typescript-eslint/prefer-namespace-keyword": "error",
288
- "@typescript-eslint/prefer-nullish-coalescing": "error",
289
- "@typescript-eslint/prefer-optional-chain": "error",
360
+ "@typescript-eslint/prefer-nullish-coalescing": [
361
+ "error",
362
+ {
363
+ "ignoreConditionalTests": true,
364
+ "ignoreTernaryTests": true,
365
+ "ignoreMixedLogicalExpressions": true
366
+ }
367
+ ], "@typescript-eslint/prefer-optional-chain": "error",
290
368
  "@typescript-eslint/prefer-readonly": "error",
291
369
  "@typescript-eslint/prefer-readonly-parameter-types": "off", // Could be useful but requires too much work and verbose notation
292
370
  "@typescript-eslint/prefer-reduce-type-parameter": "error",
@@ -299,10 +377,20 @@ module.exports = {
299
377
  "@typescript-eslint/restrict-plus-operands": [
300
378
  "error",
301
379
  {
302
- "checkCompoundAssignments": true
380
+ "checkCompoundAssignments": true,
381
+ "allowAny": false
382
+ }
383
+ ],
384
+ "@typescript-eslint/restrict-template-expressions": [
385
+ "error",
386
+ {
387
+ "allowNumber": true,
388
+ "allowBoolean": false,
389
+ "allowAny": false,
390
+ "allowNullish": false,
391
+ "allowRegExp": false
303
392
  }
304
393
  ],
305
- "@typescript-eslint/restrict-template-expressions": "error",
306
394
  "@typescript-eslint/sort-type-union-intersection-members": "off",
307
395
  "@typescript-eslint/strict-boolean-expressions": [
308
396
  "off",
package/utils/merge.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ *
3
+ * @param {any} o
4
+ * @returns o is {}
5
+ */
6
+ function isPlainObject(value) {
7
+ return !!value &&
8
+ !!(value = Object.getPrototypeOf(value)) &&
9
+ !Object.getPrototypeOf(value);
10
+ }
11
+
12
+ /**
13
+ *
14
+ * @param {any[]} first
15
+ * @param {any[]} second
16
+ * @param {boolean} insertBoth
17
+ * @returns {any[]}
18
+ */
19
+ function deepMergeArrays(first, second, insertBoth = true) {
20
+ let result = [];
21
+ for (let i = 0; i < Math.max(first.length, second.length); i++) {
22
+ if (i < second.length) {
23
+ if (i < first.length) {
24
+ if (Array.isArray(second[i])) {
25
+ if (Array.isArray(first[i])) {
26
+ result.push(deepMergeArrays(first[i], second[i]));
27
+ } else {
28
+ result.push(first[i]);
29
+ }
30
+ } else if (isPlainObject(second[i])) {
31
+ if (isPlainObject(first[i])) {
32
+ result.push(deepMergeObjects(first[i], second[i]));
33
+ } else {
34
+ result.push(first[i]);
35
+ }
36
+ } else {
37
+ if (first[i] === second[i] && !insertBoth) {
38
+ result.push(first[i]);
39
+ } else {
40
+ result.push(first[i], second[i]);
41
+ }
42
+ }
43
+ } else {
44
+ result.push(second[i]);
45
+ }
46
+ } else {
47
+ result.push(first[i]);
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+
53
+ /**
54
+ *
55
+ * @param {{}} first
56
+ * @param {{}} second
57
+ * @returns {{}}
58
+ */
59
+ function deepMergeObjects(first, second) {
60
+ const result = {};
61
+ const remainingKeysFromSecond = new Set(Object.keys(second));
62
+ for (const key of Object.keys(first)) {
63
+ if (remainingKeysFromSecond.has(key)) {
64
+ if (Array.isArray(first[key])) {
65
+ if (Array.isArray(second[key])) {
66
+ result[key] = deepMergeArrays(first[key], second[key]);
67
+ } else {
68
+ result[key] = first[key];
69
+ }
70
+ } else if (isPlainObject(first[key])) {
71
+ if (isPlainObject(second[key])) {
72
+ result[key] = deepMergeObjects(first[key], second[key]);
73
+ } else {
74
+ result[key] = first[key];
75
+ }
76
+ } else {
77
+ result[key] = first[key];
78
+ }
79
+ remainingKeysFromSecond.delete(key);
80
+ } else {
81
+ result[key] = first[key];
82
+ }
83
+ }
84
+ for (const key of Array.from(remainingKeysFromSecond)) {
85
+ result[key] = second[key];
86
+ }
87
+ return result;
88
+ }
89
+
90
+ module.exports = {
91
+ deepMergeObjects,
92
+ deepMergeArrays
93
+ };