@josundt/eslint-config 4.4.0 → 4.7.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,8 +1,8 @@
1
1
  {
2
2
  "name": "@josundt/eslint-config",
3
- "version": "4.4.0",
3
+ "version": "4.7.3",
4
4
  "description": "ESLint ruleset with required plugins for josundt TypeScript projects",
5
- "main": ".eslintrc.js",
5
+ "main": "index.js",
6
6
  "scripts": {
7
7
  "lint": "echo \"No linting for this project\"",
8
8
  "build": "echo \"No build for this project\"",
@@ -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.7.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.27.0",
34
+ "@typescript-eslint/parser": "5.27.0",
35
+ "eslint": "8.16.0",
36
+ "eslint-import-resolver-typescript": "2.7.1",
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.2",
41
+ "eslint-plugin-unicorn": "42.0.0"
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",
@@ -219,7 +231,8 @@ module.exports = {
219
231
  "error",
220
232
  {
221
233
  "vars": "all",
222
- "args": "none"
234
+ "args": "none",
235
+ "destructuredArrayIgnorePattern": "^_"
223
236
  }
224
237
  ],
225
238
  "no-use-before-define": "error",
@@ -236,12 +249,17 @@ module.exports = {
236
249
  "never"
237
250
  ],
238
251
  "padding-line-between-statements": [
239
- "off",
240
- {
241
- "blankLine": "always",
242
- "prev": "*",
243
- "next": "return"
244
- }
252
+ "error",
253
+ { "blankLine": "always", "prev": ["directive", "import"], "next": "*" },
254
+ { "blankLine": "any", "prev": ["directive", "import"], "next": ["directive", "import"] },
255
+
256
+ { "blankLine": "always", "prev": "*", "next": ["export", "class", "function", "iife"] },
257
+ { "blankLine": "always", "prev": ["export", "class", "function", "iife"], "next": "*" },
258
+
259
+ // { "blankLine": "always", "prev": "*", "next": "return" }, // Newline before return
260
+
261
+ // { "blankLine": "always", "prev": "*", "next": "multiline-block-like" }, // Newline BEFORE multiline block
262
+ // { "blankLine": "always", "prev": "multiline-block-like", "next": "*" } // Newline AFTER multiline block
245
263
  ],
246
264
  "prefer-arrow-callback": "error",
247
265
  "prefer-const": "error",
@@ -262,8 +280,12 @@ module.exports = {
262
280
  "error",
263
281
  "always"
264
282
  ],
283
+ "space-before-blocks": [
284
+ "error",
285
+ "always"
286
+ ],
265
287
  "space-before-function-paren": [
266
- "warn",
288
+ "error",
267
289
  {
268
290
  "anonymous": "always",
269
291
  "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,7 +151,17 @@ 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
123
167
  "@typescript-eslint/consistent-indexed-object-style": ["error", "record"],
@@ -132,6 +176,7 @@ module.exports = {
132
176
  "error",
133
177
  "interface"
134
178
  ],
179
+ "@typescript-eslint/consistent-type-exports": "error",
135
180
  "@typescript-eslint/consistent-type-imports": [
136
181
  "off",
137
182
  {
@@ -145,7 +190,8 @@ module.exports = {
145
190
  "allowExpressions": true,
146
191
  "allowTypedFunctionExpressions": true,
147
192
  "allowHigherOrderFunctions": true,
148
- "allowConciseArrowFunctionExpressionsStartingWithVoid": true
193
+ "allowConciseArrowFunctionExpressionsStartingWithVoid": true,
194
+ "allowedNames": []
149
195
  }
150
196
  ],
151
197
  "@typescript-eslint/explicit-member-accessibility": [
@@ -159,9 +205,9 @@ module.exports = {
159
205
  {
160
206
  "allowArgumentsExplicitlyTypedAsAny": false,
161
207
  "allowDirectConstAssertionInArrowFunctions": true,
162
- "allowedNames": [],
163
208
  "allowHigherOrderFunctions": true,
164
209
  "allowTypedFunctionExpressions": true,
210
+ "allowedNames": []
165
211
  }
166
212
  ],
167
213
  "@typescript-eslint/member-delimiter-style": [
@@ -233,6 +279,7 @@ module.exports = {
233
279
  "ignoreArrowShorthand": true
234
280
  }
235
281
  ],
282
+ "@typescript-eslint/no-duplicate-enum-values": "error",
236
283
  "@typescript-eslint/no-dynamic-delete": "error",
237
284
  "@typescript-eslint/no-empty-interface": "off",
238
285
  "@typescript-eslint/no-explicit-any": "off",
@@ -249,20 +296,36 @@ module.exports = {
249
296
  "allowAsThisParameter": true
250
297
  }
251
298
  ],
299
+ "@typescript-eslint/no-meaningless-void-operator": "error",
252
300
  "@typescript-eslint/no-misused-new": "error",
253
- "@typescript-eslint/no-misused-promises": "error",
301
+ "@typescript-eslint/no-misused-promises": [
302
+ "error",
303
+ {
304
+ checksConditionals: true,
305
+ checksSpreads: true,
306
+ checksVoidReturn: true // {
307
+ // arguments: true, //Disables checking an asynchronous function passed as argument where the parameter type expects a function that returns void
308
+ // attributes: true, //Disables checking an asynchronous function passed as a JSX attribute expected to be a function that returns void
309
+ // properties: true, //Disables checking an asynchronous function passed as an object property expected to be a function that returns void
310
+ // returns: true, //Disables checking an asynchronous function returned in a function whose return type is a function that returns void
311
+ // variables: true //Disables checking an asynchronous function used as a variable whose return type is a function that returns void
312
+ // }
313
+ }
314
+ ],
254
315
  "@typescript-eslint/no-namespace": "off",
316
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
255
317
  "@typescript-eslint/no-non-null-asserted-optional-chain": "error",
256
318
  "@typescript-eslint/no-non-null-assertion": "off",
257
- "@typescript-eslint/no-parameter-properties": [
319
+ "@typescript-eslint/no-redundant-type-constituents": "error",
320
+ "@typescript-eslint/no-require-imports": "error",
321
+ "@typescript-eslint/no-this-alias": "error",
322
+ "@typescript-eslint/no-throw-literal": [
258
323
  "error",
259
324
  {
260
- "allows": ["private readonly", "private", "protected readonly"]
325
+ "allowThrowingAny": false, // Default is to allow throwing values of type any
326
+ "allowThrowingUnknown": true // Default is to allow throwing values of type unknown
261
327
  }
262
328
  ],
263
- "@typescript-eslint/no-require-imports": "error",
264
- "@typescript-eslint/no-this-alias": "error",
265
- "@typescript-eslint/no-throw-literal": "error",
266
329
  "@typescript-eslint/no-type-alias": "off",
267
330
  "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
268
331
  "@typescript-eslint/no-unnecessary-condition": "off", // allow runtime null checks etc even if reported not necessary by type system
@@ -276,8 +339,16 @@ module.exports = {
276
339
  "@typescript-eslint/no-unsafe-member-access": "error",
277
340
  "@typescript-eslint/no-unsafe-return": "error",
278
341
  "@typescript-eslint/no-unused-vars-experimental": "off", // to strict with method params...
342
+ "@typescript-eslint/no-useless-empty-export": "error",
279
343
  "@typescript-eslint/no-var-requires": "error",
280
344
  "@typescript-eslint/non-nullable-type-assertion-style": "error",
345
+ "@typescript-eslint/parameter-properties": [
346
+ "error",
347
+ {
348
+ "prefer": "class-property", // or "parameter-property"
349
+ "allow": ["private readonly", "private", "protected readonly"]
350
+ }
351
+ ],
281
352
  "@typescript-eslint/prefer-as-const": "error",
282
353
  "@typescript-eslint/prefer-enum-initializers": "error",
283
354
  "@typescript-eslint/prefer-for-of": "error",
@@ -299,10 +370,20 @@ module.exports = {
299
370
  "@typescript-eslint/restrict-plus-operands": [
300
371
  "error",
301
372
  {
302
- "checkCompoundAssignments": true
373
+ "checkCompoundAssignments": true,
374
+ "allowAny": false
375
+ }
376
+ ],
377
+ "@typescript-eslint/restrict-template-expressions": [
378
+ "error",
379
+ {
380
+ "allowNumber": true,
381
+ "allowBoolean": false,
382
+ "allowAny": false,
383
+ "allowNullish": false,
384
+ "allowRegExp": false
303
385
  }
304
386
  ],
305
- "@typescript-eslint/restrict-template-expressions": "error",
306
387
  "@typescript-eslint/sort-type-union-intersection-members": "off",
307
388
  "@typescript-eslint/strict-boolean-expressions": [
308
389
  "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
+ };