Repository: eslint/eslint
File: tests/lib/config/flat-config-schema.js (JavaScript)

Code before:
        assert.deepStrictEqual(result, { foo: { bar: "baz", qux: 42 } });
        confirmLegacyMergeResult(first, second, result);
    });

    it("merges an object in a property with an array", () => {
        const first = { someProperty: { 1: "foo", bar: "baz" } };
        const second = { someProperty: ["qux"] };
        const result = merge(first, second);

        /*
         * When comparing two arrays, chai's deepStrictEqual does not consider non-index properties.
         * So we have to use an additional assertion:
         */
        assert.deepStrictEqual(result, { someProperty: ["qux", "foo"] });
        assert.propertyVal(result.someProperty, "bar", "baz");
        confirmLegacyMergeResult(first, second, result);
    });

    it("merges two arrays in a property", () => {
        const first = { someProperty: ["foo", "bar", void 0, "baz"] };
        const second = { someProperty: ["qux", void 0, 42] };
        const result = merge(first, second);

        assert.deepStrictEqual(result, { someProperty: ["qux", "bar", 42, "baz"] });
        confirmLegacyMergeResult(first, second, result);
    });

    it("merges an array in a property with an object", () => {
        const first = { foo: ["foobar"] };
        const second = { foo: { 1: "qux", bar: "baz" } };
        const result = merge(first, second);

        assert.deepStrictEqual(result, { foo: { 0: "foobar", 1: "qux", bar: "baz" } });
        confirmLegacyMergeResult(first, second, result);
    });

    it("does not override a value in a property with undefined", () => {
        const first = { foo: { bar: "baz" } };
        const second = { foo: void 0 };
        const result = merge(first, second);

        assert.deepStrictEqual(result, first);
        assert.notStrictEqual(result, first);
        confirmLegacyMergeResult(first, second, result);
    });

    it("does not change the prototype of a merged object", () => {
        const first = { foo: 42 };
        const second = { bar: "baz", ["__proto__"]: { qux: true } };
        const result = merge(first, second);


Review comment (bug):
This is indeed eslintrc's behavior, but I'm not sure if it was intentional or maybe an unintentional effect of an intent to just deep-clone arrays. When I update `mergeWithoutOverwrite` so that arrays overwrite arrays instead of merging, no tests fail either in the eslintrc project or the eslint project.

```diff
function mergeWithoutOverwrite(target, source) {
    if (!isNonNullObject(source)) {
        return;
    }

    for (const key of Object.keys(source)) {
        if (key === "__proto__") {
            continue;
        }

-       if (isNonNullObject(target[key])) {
+       if (isNonNullObject(target[key]) && !Array.isArray(target[key])) {
            mergeWithoutOverwrite(target[key], source[key]);
        } else if (target[key] === void 0) {
-           if (isNonNullObject(source[key])) {
-               target[key] = Array.isArray(source[key]) ? [] : {};
+           if (isNonNullObject(source[key]) && !Array.isArray(source[key])) {
+               target[key] = {};
                mergeWithoutOverwrite(target[key], source[key]);
            } else if (source[key] !== void 0) {
                target[key] = source[key];
            }
        }
    }
}
```

I'd personally rather expect that a generic deep-merging just overwrites arrays, so that this example results in `{ someProperty: ["qux", void 0, 42] }`. Merging item-by-item makes sense only when it is known that the array is meant to be used in a way that each position in the array has a specific meaning (i.e., like a tuple).

Now when searching through issues and PRs, I found that we mentioned this in https://github.com/eslint/eslint/pull/14321#discussion_r627726368.

Code after:
        assert.deepStrictEqual(result, { foo: { bar: "baz", qux: 42 } });
        confirmLegacyMergeResult(first, second, result);
    });

    it("overwrites an object in a property with an array", () => {
        const first = { someProperty: { 1: "foo", bar: "baz" } };
        const second = { someProperty: ["qux"] };
        const result = merge(first, second);

        assert.deepStrictEqual(result, second);
        assert.strictEqual(result.someProperty, second.someProperty);
    });

    it("overwrites an array in a property with another array", () => {
        const first = { someProperty: ["foo", "bar", void 0, "baz"] };
        const second = { someProperty: ["qux", void 0, 42] };
        const result = merge(first, second);

        assert.deepStrictEqual(result, second);
        assert.strictEqual(result.someProperty, second.someProperty);
    });

    it("overwrites an array in a property with an object", () => {
        const first = { foo: ["foobar"] };
        const second = { foo: { 1: "qux", bar: "baz" } };
        const result = merge(first, second);

        assert.deepStrictEqual(result, second);
        assert.strictEqual(result.foo, second.foo);
    });

    it("does not override a value in a property with undefined", () => {
        const first = { foo: { bar: "baz" } };
        const second = { foo: void 0 };
        const result = merge(first, second);

        assert.deepStrictEqual(result, first);
        assert.notStrictEqual(result, first);
        confirmLegacyMergeResult(first, second, result);
    });

    it("does not change the prototype of a merged object", () => {
        const first = { foo: 42 };
        const second = { bar: "baz", ["__proto__"]: { qux: true } };
        const result = merge(first, second);

        assert.strictEqual(Object.getPrototypeOf(result), Object.prototype);
        confirmLegacyMergeResult(first, second, result);
    });

    it("does not merge the '__proto__' property", () => {