@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 +15 -13
- package/rules/eslint.js +40 -12
- package/rules/import-typescript.js +2 -1
- package/rules/typescript-eslint.js +114 -26
- package/utils/merge.js +93 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@josundt/eslint-config",
|
3
|
-
"version": "4.
|
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.
|
30
|
+
"typescript": ">=4.8.2"
|
30
31
|
},
|
31
32
|
"dependencies": {
|
32
|
-
"@typescript-eslint/eslint-plugin": "
|
33
|
-
"@typescript-eslint/parser": "
|
34
|
-
"eslint": "
|
35
|
-
"eslint-import-resolver-typescript": "
|
36
|
-
"eslint-plugin-deprecation": "1.2
|
37
|
-
"eslint-plugin-import": "2.
|
38
|
-
"eslint-plugin-jasmine": "4.1.
|
39
|
-
"eslint-plugin-jsdoc": "
|
40
|
-
"eslint-plugin-unicorn": "
|
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-
|
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
|
-
"
|
240
|
-
{
|
241
|
-
|
242
|
-
|
243
|
-
|
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
|
-
"
|
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
|
-
"
|
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
|
-
//
|
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-
|
27
|
-
|
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
|
-
|
36
|
-
|
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
|
77
|
-
value
|
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":
|
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":
|
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-
|
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
|
-
"
|
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/
|
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":
|
289
|
-
|
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
|
+
};
|