From 1c424f4db1439f81c6657b460e37bc2b81412a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 20 Jan 2021 09:18:49 +0100 Subject: [PATCH] add deep merge --- packages/core/src/Extension.ts | 6 +- packages/core/src/Mark.ts | 6 +- packages/core/src/Node.ts | 7 +- packages/core/src/utilities/isObject.ts | 3 + packages/core/src/utilities/mergeDeep.ts | 22 ++++++ .../integration/core/mergeDeep.spec.ts | 68 +++++++++++++++++++ 6 files changed, 100 insertions(+), 12 deletions(-) create mode 100644 packages/core/src/utilities/isObject.ts create mode 100644 packages/core/src/utilities/mergeDeep.ts create mode 100644 tests/cypress/integration/core/mergeDeep.spec.ts diff --git a/packages/core/src/Extension.ts b/packages/core/src/Extension.ts index 41e29459..da6eb8a5 100644 --- a/packages/core/src/Extension.ts +++ b/packages/core/src/Extension.ts @@ -1,6 +1,7 @@ import { Plugin, Transaction } from 'prosemirror-state' import { InputRule } from 'prosemirror-inputrules' import { Editor } from './Editor' +import mergeDeep from './utilities/mergeDeep' import { GlobalAttributes } from './types' export interface ExtensionConfig { @@ -178,10 +179,7 @@ export class Extension { } #configure = (options: Partial) => { - this.options = { - ...this.config.defaultOptions, - ...options, - } + this.options = mergeDeep(this.config.defaultOptions, options) as Options return this } diff --git a/packages/core/src/Mark.ts b/packages/core/src/Mark.ts index 230fc643..801d395e 100644 --- a/packages/core/src/Mark.ts +++ b/packages/core/src/Mark.ts @@ -7,6 +7,7 @@ import { import { Plugin, Transaction } from 'prosemirror-state' import { InputRule } from 'prosemirror-inputrules' import { ExtensionConfig } from './Extension' +import mergeDeep from './utilities/mergeDeep' import { Attributes, Overwrite } from './types' import { Editor } from './Editor' @@ -238,10 +239,7 @@ export class Mark { } #configure = (options: Partial) => { - this.options = { - ...this.config.defaultOptions, - ...options, - } + this.options = mergeDeep(this.config.defaultOptions, options) as Options return this } diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index b831dfc3..f5ba9cc2 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import { DOMOutputSpec, NodeSpec, @@ -7,6 +8,7 @@ import { import { Plugin, Transaction } from 'prosemirror-state' import { InputRule } from 'prosemirror-inputrules' import { ExtensionConfig } from './Extension' +import mergeDeep from './utilities/mergeDeep' import { Attributes, NodeViewRenderer, Overwrite } from './types' import { Editor } from './Editor' @@ -305,10 +307,7 @@ export class Node { } #configure = (options: Partial) => { - this.options = { - ...this.config.defaultOptions, - ...options, - } + this.options = mergeDeep(this.config.defaultOptions, options) as Options return this } diff --git a/packages/core/src/utilities/isObject.ts b/packages/core/src/utilities/isObject.ts new file mode 100644 index 00000000..99d1f367 --- /dev/null +++ b/packages/core/src/utilities/isObject.ts @@ -0,0 +1,3 @@ +export default function isObject(item: any): boolean { + return (item && typeof item === 'object' && !Array.isArray(item)) +} diff --git a/packages/core/src/utilities/mergeDeep.ts b/packages/core/src/utilities/mergeDeep.ts new file mode 100644 index 00000000..d2c400d1 --- /dev/null +++ b/packages/core/src/utilities/mergeDeep.ts @@ -0,0 +1,22 @@ +import { AnyObject } from '../types' +import isObject from './isObject' + +export default function mergeDeep(target: AnyObject, source: AnyObject) { + const output = { ...target } + + if (isObject(target) && isObject(source)) { + Object.keys(source).forEach(key => { + if (isObject(source[key])) { + if (!(key in target)) { + Object.assign(output, { [key]: source[key] }) + } else { + output[key] = mergeDeep(target[key], source[key]) + } + } else { + Object.assign(output, { [key]: source[key] }) + } + }) + } + + return output +} diff --git a/tests/cypress/integration/core/mergeDeep.spec.ts b/tests/cypress/integration/core/mergeDeep.spec.ts new file mode 100644 index 00000000..01f26fde --- /dev/null +++ b/tests/cypress/integration/core/mergeDeep.spec.ts @@ -0,0 +1,68 @@ +/// + +import mergeDeep from '@tiptap/core/src/utilities/mergeDeep' + +describe('mergeDeep', () => { + it('should merge', () => { + const one = { + a: 1, + } + const two = { + b: 1, + } + const result = { + a: 1, + b: 1, + } + const merged = mergeDeep(one, two) + + expect(merged).to.deep.eq(result) + }) + + it('should not merge array', () => { + const one = { + a: [1], + } + const two = { + a: [2], + } + const result = { + a: [2], + } + const merged = mergeDeep(one, two) + + expect(merged).to.deep.eq(result) + }) + + it('should merge deep', () => { + const one = { + a: 1, + b: { + c: true, + }, + d: { + e: true, + f: [1], + }, + } + const two = { + b: 1, + d: { + f: [2], + g: 1, + }, + } + const result = { + a: 1, + b: 1, + d: { + e: true, + f: [2], + g: 1, + }, + } + const merged = mergeDeep(one, two) + + expect(merged).to.deep.eq(result) + }) +})