From 8c3c2e7e3fb119464e3e63af17188c2bc478572b Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Thu, 14 Oct 2021 14:37:00 +0200 Subject: [PATCH] refactor(v4): Base migration --- admin/src/index.js | 99 ++++++++++--------- config/config.json | 13 --- config/routes.json | 28 ------ {config/functions => server}/bootstrap.js | 0 server/config.js | 18 ++++ {controllers => server/controllers}/config.js | 19 ++-- server/controllers/index.js | 7 ++ server/routes/admin.js | 31 ++++++ server/routes/index.js | 7 ++ {services => server/services}/core-store.js | 14 +-- {services => server/services}/i18n-locale.js | 33 +++---- server/services/index.js | 15 +++ {services => server/services}/main.js | 31 +++--- .../services}/role-permissions.js | 32 +++--- {services => server/services}/webhooks.js | 0 server/utils/getObjectDiff.js | 30 ++++++ server/utils/index.js | 17 ++++ strapi-admin.js | 3 + strapi-server.js | 17 ++++ utils/getObjectDiff.js | 33 ------- 20 files changed, 265 insertions(+), 182 deletions(-) delete mode 100644 config/config.json delete mode 100644 config/routes.json rename {config/functions => server}/bootstrap.js (100%) create mode 100644 server/config.js rename {controllers => server/controllers}/config.js (77%) create mode 100644 server/controllers/index.js create mode 100644 server/routes/admin.js create mode 100644 server/routes/index.js rename {services => server/services}/core-store.js (95%) rename {services => server/services}/i18n-locale.js (88%) create mode 100644 server/services/index.js rename {services => server/services}/main.js (86%) rename {services => server/services}/role-permissions.js (89%) rename {services => server/services}/webhooks.js (100%) create mode 100644 server/utils/getObjectDiff.js create mode 100644 server/utils/index.js create mode 100644 strapi-admin.js create mode 100644 strapi-server.js delete mode 100644 utils/getObjectDiff.js diff --git a/admin/src/index.js b/admin/src/index.js index 52cb61c..c01b112 100644 --- a/admin/src/index.js +++ b/admin/src/index.js @@ -1,53 +1,62 @@ -import React from 'react'; +import { prefixPluginTranslations } from '@strapi/helper-plugin'; import pluginPkg from '../../package.json'; import pluginId from './helpers/pluginId'; -import App from './containers/App'; -import Initializer from './containers/Initializer'; -import trads from './translations'; +// import pluginPermissions from './permissions'; +// import getTrad from './helpers/getTrad'; -function Comp(props) { - return ; -} +const pluginDescription = pluginPkg.strapi.description || pluginPkg.description; +const { icon, name } = pluginPkg.strapi; -export default strapi => { - const pluginDescription = - pluginPkg.strapi.description || pluginPkg.description; +export default { + register(app) { + app.registerPlugin({ + description: pluginDescription, + icon, + id: pluginId, + isReady: true, + isRequired: pluginPkg.strapi.required || false, + name, + }); - const icon = pluginPkg.strapi.icon; - const name = pluginPkg.strapi.name; + app.addMenuLink({ + to: `/plugins/${pluginId}`, + icon, + intlLabel: { + id: `${pluginId}.plugin.name`, + defaultMessage: 'Config Sync', + }, + Component: async () => { + const component = await import( + /* webpackChunkName: "config-sync-settings-page" */ './containers/App' + ); - const plugin = { - icon, - name, - destination: `/plugins/${pluginId}`, - blockerComponent: null, - blockerComponentProps: {}, - description: pluginDescription, - id: pluginId, - initializer: Initializer, - injectedComponents: [], - isReady: false, - layout: null, - leftMenuLinks: [], - leftMenuSections: [], - mainComponent: Comp, - name: pluginPkg.strapi.name, - preventComponentRendering: false, - trads, - menu: { - pluginsSectionLinks: [ - { - destination: `/plugins/${pluginId}`, // Endpoint of the link - icon, - name, - label: { - id: `${pluginId}.plugin.name`, // Refers to a i18n - defaultMessage: 'Config Sync', - }, - }, - ], - }, - }; + return component; + }, + permissions: [], // TODO: Add permission to view settings page. + }); + }, + bootstrap(app) {}, + async registerTrads({ locales }) { + const importedTrads = await Promise.all( + locales.map((locale) => { + return import( + /* webpackChunkName: "config-sync-translation-[request]" */ `./translations/${locale}.json` + ) + .then(({ default: data }) => { + return { + data: prefixPluginTranslations(data, pluginId), + locale, + }; + }) + .catch(() => { + return { + data: {}, + locale, + }; + }); + }) + ); - return strapi.registerPlugin(plugin); + return Promise.resolve(importedTrads); + }, }; diff --git a/config/config.json b/config/config.json deleted file mode 100644 index d7732f6..0000000 --- a/config/config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "destination": "extensions/config-sync/files/", - "minify": false, - "importOnBootstrap": false, - "include": [ - "core-store", - "role-permissions", - "i18n-locale" - ], - "exclude": [ - "core-store.plugin_users-permissions_grant" - ] -} diff --git a/config/routes.json b/config/routes.json deleted file mode 100644 index eaa1a97..0000000 --- a/config/routes.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "routes": [ - { - "method": "GET", - "path": "/export", - "handler": "config.exportAll", - "config": { - "policies": [] - } - }, - { - "method": "GET", - "path": "/import", - "handler": "config.importAll", - "config": { - "policies": [] - } - }, - { - "method": "GET", - "path": "/diff", - "handler": "config.getDiff", - "config": { - "policies": [] - } - } - ] -} diff --git a/config/functions/bootstrap.js b/server/bootstrap.js similarity index 100% rename from config/functions/bootstrap.js rename to server/bootstrap.js diff --git a/server/config.js b/server/config.js new file mode 100644 index 0000000..067c4c0 --- /dev/null +++ b/server/config.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = { + default: { + destination: "extensions/config-sync/files/", + minify: false, + importOnBootstrap: false, + include: [ + "core-store", + "role-permissions", + "i18n-locale", + ], + exclude: [ + "core-store.plugin_users-permissions_grant", + ], + }, + validator() {}, +}; diff --git a/controllers/config.js b/server/controllers/config.js similarity index 77% rename from controllers/config.js rename to server/controllers/config.js index 451bb8c..e1ea90a 100644 --- a/controllers/config.js +++ b/server/controllers/config.js @@ -18,7 +18,7 @@ module.exports = { await strapi.plugins['config-sync'].services.main.exportAllConfig(); ctx.send({ - message: `Config was successfully exported to ${strapi.plugins['config-sync'].config.destination}.` + message: `Config was successfully exported to ${strapi.plugins['config-sync'].config.destination}.`, }); }, @@ -32,16 +32,16 @@ module.exports = { // Check for existance of the config file destination dir. if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) { ctx.send({ - message: 'No config files were found.' + message: 'No config files were found.', }); return; } - + await strapi.plugins['config-sync'].services.main.importAllConfig(); ctx.send({ - message: 'Config was successfully imported.' + message: 'Config was successfully imported.', }); }, @@ -49,13 +49,16 @@ module.exports = { * Get config diff between filesystem & db. * * @param {object} ctx - Request context object. - * @returns Object with key value pairs of config. + * @returns {object} formattedDiff - The formatted diff object. + * @returns {object} formattedDiff.fileConfig - The config as found in the filesystem. + * @returns {object} formattedDiff.databaseConfig - The config as found in the database. + * @returns {object} formattedDiff.diff - The diff between the file config and databse config. */ getDiff: async (ctx) => { // Check for existance of the config file destination dir. if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) { ctx.send({ - message: 'No config files were found.' + message: 'No config files were found.', }); return; @@ -64,9 +67,9 @@ module.exports = { const formattedDiff = { fileConfig: {}, databaseConfig: {}, - diff: {} + diff: {}, }; - + const fileConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromFiles(); const databaseConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromDatabase(); diff --git a/server/controllers/index.js b/server/controllers/index.js new file mode 100644 index 0000000..9350e3b --- /dev/null +++ b/server/controllers/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const config = require('./config'); + +module.exports = { + config: config, +}; diff --git a/server/routes/admin.js b/server/routes/admin.js new file mode 100644 index 0000000..aae8fe3 --- /dev/null +++ b/server/routes/admin.js @@ -0,0 +1,31 @@ +'use strict'; + +module.exports = { + type: 'admin', + routes: [ + { + method: "GET", + path: "/export", + handler: "config.exportAll", + config: { + policies: [], + }, + }, + { + method: "GET", + path: "/import", + handler: "config.importAll", + config: { + policies: [], + }, + }, + { + method: "GET", + path: "/diff", + handler: "config.getDiff", + config: { + policies: [], + }, + }, + ], +}; diff --git a/server/routes/index.js b/server/routes/index.js new file mode 100644 index 0000000..01cd624 --- /dev/null +++ b/server/routes/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const adminRoutes = require('./admin'); + +module.exports = { + admin: adminRoutes, +}; diff --git a/services/core-store.js b/server/services/core-store.js similarity index 95% rename from services/core-store.js rename to server/services/core-store.js index 64037ad..ad22720 100644 --- a/services/core-store.js +++ b/server/services/core-store.js @@ -18,9 +18,9 @@ module.exports = { const formattedDiff = { fileConfig: {}, databaseConfig: {}, - diff: {} + diff: {}, }; - + const fileConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromFiles(configPrefix); const databaseConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromDatabase(configPrefix); const diff = difference(databaseConfig, fileConfig); @@ -30,7 +30,7 @@ module.exports = { Object.keys(diff).map((changedConfigName) => { formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName]; formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName]; - }) + }); await Promise.all(Object.entries(diff).map(async ([configName, config]) => { // Check if the config should be excluded. @@ -40,8 +40,8 @@ module.exports = { const currentConfig = formattedDiff.databaseConfig[configName]; if ( - !currentConfig && - formattedDiff.fileConfig[configName] + !currentConfig + && formattedDiff.fileConfig[configName] ) { await strapi.plugins['config-sync'].services.main.deleteConfigFile(configName); } else { @@ -89,9 +89,9 @@ module.exports = { */ getAllFromDatabase: async () => { const coreStore = await strapi.query(coreStoreQueryString).find({ _limit: -1 }); - let configs = {}; + const configs = {}; - Object.values(coreStore).map( ({ id, value, key, ...config }) => { + Object.values(coreStore).map(({ id, value, key, ...config }) => { // Check if the config should be excluded. const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configPrefix}.${key}`); if (shouldExclude) return; diff --git a/services/i18n-locale.js b/server/services/i18n-locale.js similarity index 88% rename from services/i18n-locale.js rename to server/services/i18n-locale.js index 5b31999..c20a213 100644 --- a/services/i18n-locale.js +++ b/server/services/i18n-locale.js @@ -1,10 +1,11 @@ 'use strict'; -const i18nQueryString = 'i18n_locales'; +const { sanitizeEntity } = require('@strapi/utils'); + +// const i18nQueryString = 'i18n_locales'; const configPrefix = 'i18n-locale'; // Should be the same as the filename. const difference = require('../utils/getObjectDiff'); -const { sanitizeEntity } = require('strapi-utils'); /** * Import/Export for i18n-locale configs. @@ -20,9 +21,9 @@ module.exports = { const formattedDiff = { fileConfig: {}, databaseConfig: {}, - diff: {} + diff: {}, }; - + const fileConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromFiles(configPrefix); const databaseConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromDatabase(configPrefix); const diff = difference(databaseConfig, fileConfig); @@ -32,7 +33,7 @@ module.exports = { Object.keys(diff).map((changedConfigName) => { formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName]; formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName]; - }) + }); await Promise.all(Object.entries(diff).map(async ([configName, config]) => { // Check if the config should be excluded. @@ -42,8 +43,8 @@ module.exports = { const currentConfig = formattedDiff.databaseConfig[configName]; if ( - !currentConfig && - formattedDiff.fileConfig[configName] + !currentConfig + && formattedDiff.fileConfig[configName] ) { await strapi.plugins['config-sync'].services.main.deleteConfigFile(configName); } else { @@ -64,9 +65,8 @@ module.exports = { const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configPrefix}.${configName}`); if (shouldExclude) return; - const service = - strapi.plugins['i18n'].services.locales; - + const service = strapi.plugins['i18n'].services.locales; + const locale = await service.findByCode(configName); if (locale && configContent === null) { @@ -87,19 +87,18 @@ module.exports = { * @returns {object} Object with code value pairs of configs. */ getAllFromDatabase: async () => { - const service = - strapi.plugins['i18n'].services.locales; + const service = strapi.plugins['i18n'].services.locales; const locales = await service.find({ _limit: -1 }); - let configs = {}; + const configs = {}; - const sanitizedLocalesArray = locales.map(locale => + const sanitizedLocalesArray = locales.map((locale) => { sanitizeEntity(locale, { model: strapi.plugins['i18n'].models.locale, - }) - ); + }); + }); - Object.values(sanitizedLocalesArray).map( ({ id, code, ...config }) => { + Object.values(sanitizedLocalesArray).map(({ id, code, ...config }) => { // Check if the config should be excluded. const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configPrefix}.${code}`); if (shouldExclude) return; diff --git a/server/services/index.js b/server/services/index.js new file mode 100644 index 0000000..b72085e --- /dev/null +++ b/server/services/index.js @@ -0,0 +1,15 @@ +'use strict'; + +const main = require('./main'); +const coreStore = require('./core-store'); +const i18nLocale = require('./i18n-locale'); +const rolePermissions = require('./role-permissions'); +const webhooks = require('./webhooks'); + +module.exports = { + main, + 'role-permissions': rolePermissions, + 'i18n-locale': i18nLocale, + 'core-store': coreStore, + webhooks, +}; diff --git a/services/main.js b/server/services/main.js similarity index 86% rename from services/main.js rename to server/services/main.js index 6d3ab29..3813e45 100644 --- a/services/main.js +++ b/server/services/main.js @@ -23,10 +23,9 @@ module.exports = { if (shouldExclude) return; // Check if the JSON content should be minified. - const json = - !strapi.plugins['config-sync'].config.minify ? - JSON.stringify(fileContents, null, 2) - : JSON.stringify(fileContents); + const json = !strapi.plugins['config-sync'].config.minify + ? JSON.stringify(fileContents, null, 2) + : JSON.stringify(fileContents); if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) { fs.mkdirSync(strapi.plugins['config-sync'].config.destination, { recursive: true }); @@ -67,7 +66,7 @@ module.exports = { */ readConfigFile: async (configType, configName) => { const readFile = util.promisify(fs.readFile); - return await readFile(`${strapi.plugins['config-sync'].config.destination}${configType}.${configName}.json`) + return readFile(`${strapi.plugins['config-sync'].config.destination}${configType}.${configName}.json`) .then((data) => { return JSON.parse(data); }) @@ -80,6 +79,7 @@ module.exports = { /** * Get all the config JSON from the filesystem. * + * @param {string} configType - Type of config to gather. Leave empty to get all config. * @returns {object} Object with key value pairs of configs. */ getAllConfigFromFiles: async (configType = null) => { @@ -90,16 +90,16 @@ module.exports = { const configFiles = fs.readdirSync(strapi.plugins['config-sync'].config.destination); const getConfigs = async () => { - let fileConfigs = {}; + const fileConfigs = {}; await Promise.all(configFiles.map(async (file) => { const type = file.split('.')[0].replace('##', '::'); // Grab the first part of the filename. const name = file.split(/\.(.+)/)[1].split('.').slice(0, -1).join('.').replace('##', '::'); // Grab the rest of the filename minus the file extension. if ( - configType && configType !== type || - !strapi.plugins['config-sync'].config.include.includes(type) || - strapi.plugins['config-sync'].config.exclude.includes(`${type}.${name}`) + configType && configType !== type + || !strapi.plugins['config-sync'].config.include.includes(type) + || strapi.plugins['config-sync'].config.exclude.includes(`${type}.${name}`) ) { return; } @@ -111,18 +111,19 @@ module.exports = { return fileConfigs; }; - return await getConfigs(); + return getConfigs(); }, /** * Get all the config JSON from the database. * + * @param {string} configType - Type of config to gather. Leave empty to get all config. * @returns {object} Object with key value pairs of configs. */ getAllConfigFromDatabase: async (configType = null) => { const getConfigs = async () => { let databaseConfigs = {}; - + await Promise.all(strapi.plugins['config-sync'].config.include.map(async (type) => { if (configType && configType !== type) { return; @@ -133,14 +134,15 @@ module.exports = { })); return databaseConfigs; - } + }; - return await getConfigs(); + return getConfigs(); }, /** * Import all config files into the db. * + * @param {string} configType - Type of config to impor. Leave empty to import all config. * @returns {void} */ importAllConfig: async (configType = null) => { @@ -164,6 +166,7 @@ module.exports = { /** * Export all config files. * + * @param {string} configType - Type of config to export. Leave empty to export all config. * @returns {void} */ exportAllConfig: async (configType = null) => { @@ -201,6 +204,6 @@ module.exports = { * @returns {void} */ exportSingleConfig: async (configType, configName) => { - + }, }; diff --git a/services/role-permissions.js b/server/services/role-permissions.js similarity index 89% rename from services/role-permissions.js rename to server/services/role-permissions.js index fe076b3..9d1812d 100644 --- a/services/role-permissions.js +++ b/server/services/role-permissions.js @@ -1,6 +1,6 @@ 'use strict'; -const { sanitizeEntity } = require('strapi-utils'); +const { sanitizeEntity } = require('@strapi/utils'); const difference = require('../utils/getObjectDiff'); const configPrefix = 'role-permissions'; // Should be the same as the filename. @@ -19,9 +19,9 @@ module.exports = { const formattedDiff = { fileConfig: {}, databaseConfig: {}, - diff: {} + diff: {}, }; - + const fileConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromFiles(configPrefix); const databaseConfig = await strapi.plugins['config-sync'].services.main.getAllConfigFromDatabase(configPrefix); const diff = difference(databaseConfig, fileConfig); @@ -31,7 +31,7 @@ module.exports = { Object.keys(diff).map((changedConfigName) => { formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName]; formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName]; - }) + }); await Promise.all(Object.entries(diff).map(async ([configName, config]) => { // Check if the config should be excluded. @@ -41,8 +41,8 @@ module.exports = { const currentConfig = formattedDiff.databaseConfig[configName]; if ( - !currentConfig && - formattedDiff.fileConfig[configName] + !currentConfig + && formattedDiff.fileConfig[configName] ) { await strapi.plugins['config-sync'].services.main.deleteConfigFile(configName); } else { @@ -64,9 +64,8 @@ module.exports = { const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configPrefix}.${configName}`); if (shouldExclude) return; - const service = - strapi.plugins['users-permissions'].services.userspermissions; - + const service = strapi.plugins['users-permissions'].services.userspermissions; + const role = await strapi .query('role', 'users-permissions') .findOne({ type: configName }); @@ -96,8 +95,7 @@ module.exports = { * @returns {object} Object with key value pairs of configs. */ getAllFromDatabase: async () => { - const service = - strapi.plugins['users-permissions'].services.userspermissions; + const service = strapi.plugins['users-permissions'].services.userspermissions; const [roles, plugins] = await Promise.all([ service.getRoles(), @@ -105,22 +103,22 @@ module.exports = { ]); const rolesWithPermissions = await Promise.all( - roles.map(async role => service.getRole(role.id, plugins)) + roles.map(async (role) => service.getRole(role.id, plugins)) ); - const sanitizedRolesArray = rolesWithPermissions.map(role => + const sanitizedRolesArray = rolesWithPermissions.map((role) => { sanitizeEntity(role, { model: strapi.plugins['users-permissions'].models.role, - }) - ); + }); + }); - let configs = {}; + const configs = {}; Object.values(sanitizedRolesArray).map(({ id, ...config }) => { // Check if the config should be excluded. const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configPrefix}.${config.type}`); if (shouldExclude) return; - + // Do not export the _id field, as it is immutable delete config._id; diff --git a/services/webhooks.js b/server/services/webhooks.js similarity index 100% rename from services/webhooks.js rename to server/services/webhooks.js diff --git a/server/utils/getObjectDiff.js b/server/utils/getObjectDiff.js new file mode 100644 index 0000000..d741ce5 --- /dev/null +++ b/server/utils/getObjectDiff.js @@ -0,0 +1,30 @@ +'use strict'; + +const { transform, isEqual, isArray, isObject } = require('lodash'); + +/** + * Find difference between two objects + * @param {object} origObj - Source object to compare newObj against + * @param {object} newObj - New object with potential changes + * @return {object} differences + */ +const difference = (origObj, newObj) => { + let arrayIndexCounter = 0; + + const newObjChange = transform(newObj, (result, value, key) => { + if (!isEqual(value, origObj[key])) { + const resultKey = isArray(origObj) ? arrayIndexCounter++ : key; + result[resultKey] = (isObject(value) && isObject(origObj[key])) ? difference(value, origObj[key]) : value; + } + }); + const origObjChange = transform(origObj, (result, value, key) => { + if (!isEqual(value, newObj[key])) { + const resultKey = isArray(newObj) ? arrayIndexCounter++ : key; + result[resultKey] = (isObject(value) && isObject(newObj[key])) ? difference(value, newObj[key]) : value; + } + }); + + return Object.assign(newObjChange, origObjChange); +}; + +module.exports = difference; diff --git a/server/utils/index.js b/server/utils/index.js new file mode 100644 index 0000000..7d4b6ac --- /dev/null +++ b/server/utils/index.js @@ -0,0 +1,17 @@ +'use strict'; + +const getCoreStore = () => { + return strapi.store({ type: 'plugin', name: 'config-sync' }); +}; + +const getService = (name) => { + return strapi.plugin('config-sync').service(name); +}; + +const logMessage = (msg = '') => `[strapi-plugin-config-sync]: ${msg}`; + +module.exports = { + getService, + getCoreStore, + logMessage, +}; diff --git a/strapi-admin.js b/strapi-admin.js new file mode 100644 index 0000000..2d1a3d9 --- /dev/null +++ b/strapi-admin.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./admin/src').default; diff --git a/strapi-server.js b/strapi-server.js new file mode 100644 index 0000000..23b458a --- /dev/null +++ b/strapi-server.js @@ -0,0 +1,17 @@ +'use strict'; + +const bootstrap = require('./server/bootstrap'); +const services = require('./server/services'); +const routes = require('./server/routes'); +const config = require('./server/config'); +const controllers = require('./server/controllers'); + +module.exports = () => { + return { + bootstrap, + routes, + config, + controllers, + services, + }; +}; diff --git a/utils/getObjectDiff.js b/utils/getObjectDiff.js deleted file mode 100644 index 7176ac2..0000000 --- a/utils/getObjectDiff.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; -const { transform, isEqual, isArray, isObject } = require('lodash'); - -/** - * Find difference between two objects - * @param {object} origObj - Source object to compare newObj against - * @param {object} newObj - New object with potential changes - * @return {object} differences - */ -const difference = (origObj, newObj) => { - function changes(newObj, origObj) { - let arrayIndexCounter = 0 - - const newObjChange = transform(newObj, function (result, value, key) { - if (!isEqual(value, origObj[key])) { - let resultKey = isArray(origObj) ? arrayIndexCounter++ : key - result[resultKey] = (isObject(value) && isObject(origObj[key])) ? changes(value, origObj[key]) : value - } - }); - const origObjChange = transform(origObj, function (result, value, key) { - if (!isEqual(value, newObj[key])) { - let resultKey = isArray(newObj) ? arrayIndexCounter++ : key - result[resultKey] = (isObject(value) && isObject(newObj[key])) ? changes(value, newObj[key]) : value - } - }) - - return Object.assign(newObjChange, origObjChange); - } - - return changes(newObj, origObj) -} - -module.exports = difference; \ No newline at end of file