Changed data model to items and props having an id
This commit is contained in:
parent
0f7c601635
commit
ab8d4b4be6
|
@ -53,10 +53,10 @@ export default class JSONNode extends Component {
|
||||||
if (data.expanded) {
|
if (data.expanded) {
|
||||||
if (data.props.length > 0) {
|
if (data.props.length > 0) {
|
||||||
const props = data.props.map(prop => {
|
const props = data.props.map(prop => {
|
||||||
return h('li', {key : prop.name},
|
return h('li', {key: prop.id},
|
||||||
h(this.constructor, {
|
h(this.constructor, {
|
||||||
parent: this,
|
parent: this,
|
||||||
prop: prop,
|
prop,
|
||||||
data: prop.value,
|
data: prop.value,
|
||||||
options,
|
options,
|
||||||
events
|
events
|
||||||
|
@ -78,6 +78,7 @@ export default class JSONNode extends Component {
|
||||||
return h('div', {}, [node, childs])
|
return h('div', {}, [node, childs])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: extract a function renderChilds shared by both renderJSONObject and renderJSONArray (rename .props and .items to .childs?)
|
||||||
renderJSONArray ({prop, index, data, options, events}) {
|
renderJSONArray ({prop, index, data, options, events}) {
|
||||||
const childCount = data.items.length
|
const childCount = data.items.length
|
||||||
const node = h('div', {name: compileJSONPointer(this.getPath()), key: 'node', className: 'jsoneditor-node jsoneditor-array'}, [
|
const node = h('div', {name: compileJSONPointer(this.getPath()), key: 'node', className: 'jsoneditor-node jsoneditor-array'}, [
|
||||||
|
@ -91,12 +92,12 @@ export default class JSONNode extends Component {
|
||||||
let childs
|
let childs
|
||||||
if (data.expanded) {
|
if (data.expanded) {
|
||||||
if (data.items.length > 0) {
|
if (data.items.length > 0) {
|
||||||
const items = data.items.map((child, index) => {
|
const items = data.items.map((item, index) => {
|
||||||
return h('li', {key : index},
|
return h('li', {key : item.id},
|
||||||
h(this.constructor, {
|
h(this.constructor, {
|
||||||
parent: this,
|
parent: this,
|
||||||
index,
|
index,
|
||||||
data: child,
|
data: item.value,
|
||||||
options,
|
options,
|
||||||
events
|
events
|
||||||
})
|
})
|
||||||
|
@ -287,8 +288,6 @@ export default class JSONNode extends Component {
|
||||||
target = target.parentNode
|
target = target.parentNode
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('value', this.props)
|
|
||||||
|
|
||||||
target.className = JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
|
target.className = JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
|
||||||
JSONNode.getSearchResultClass(this.props.data.searchResult)
|
JSONNode.getSearchResultClass(this.props.data.searchResult)
|
||||||
target.title = itsAnUrl ? JSONNode.URL_TITLE : ''
|
target.title = itsAnUrl ? JSONNode.URL_TITLE : ''
|
||||||
|
|
|
@ -118,16 +118,11 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// enrich the data with search results
|
// enrich the data with search results
|
||||||
|
// TODO: performance improvements in search would be nice though it's acceptable right now
|
||||||
const searchResults = this.state.search.text ? search(data, this.state.search.text) : null
|
const searchResults = this.state.search.text ? search(data, this.state.search.text) : null
|
||||||
if (searchResults) {
|
if (searchResults) {
|
||||||
data = addSearchResults(data, searchResults, this.state.search.active)
|
data = addSearchResults(data, searchResults, this.state.search.active)
|
||||||
}
|
}
|
||||||
// TODO: moveTo active search result (not focus!)
|
|
||||||
// if (this.state.search.active) {
|
|
||||||
// data = addFocus(data, this.state.search.active)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.log('data', data)
|
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
className: `jsoneditor jsoneditor-mode-${props.mode}`,
|
className: `jsoneditor jsoneditor-mode-${props.mode}`,
|
||||||
|
|
231
src/jsonData.js
231
src/jsonData.js
|
@ -9,7 +9,10 @@ import { setIn, updateIn, getIn, deleteIn, insertAt } from './utils/immutability
|
||||||
import { isObject } from './utils/typeUtils'
|
import { isObject } from './utils/typeUtils'
|
||||||
import isEqual from 'lodash/isEqual'
|
import isEqual from 'lodash/isEqual'
|
||||||
|
|
||||||
import type {JSONData, DataPointer, Path} from './types'
|
import type {
|
||||||
|
JSONData, ObjectData, ItemData, DataPointer, Path,
|
||||||
|
JSONPatch, JSONPatchAction, PatchOptions, JSONPatchResult
|
||||||
|
} from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand function which will expand all nodes
|
* Expand function which will expand all nodes
|
||||||
|
@ -33,15 +36,21 @@ export function jsonToData (json, expand = expandAll, path = [], type = 'value')
|
||||||
return {
|
return {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: expand(path),
|
expanded: expand(path),
|
||||||
items: json.map((child, index) => jsonToData(child, expand, path.concat(index)))
|
items: json.map((child, index) => {
|
||||||
|
return {
|
||||||
|
id: getId(), // TODO: use id based on index (only has to be unique within this array)
|
||||||
|
value: jsonToData(child, expand, path.concat(index))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isObject(json)) {
|
else if (isObject(json)) {
|
||||||
return {
|
return {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: expand(path),
|
expanded: expand(path),
|
||||||
props: Object.keys(json).map(name => {
|
props: Object.keys(json).map((name, index) => {
|
||||||
return {
|
return {
|
||||||
|
id: getId(), // TODO: use id based on index (only has to be unique within this array)
|
||||||
name,
|
name,
|
||||||
value: jsonToData(json[name], expand, path.concat(name))
|
value: jsonToData(json[name], expand, path.concat(name))
|
||||||
}
|
}
|
||||||
|
@ -61,10 +70,10 @@ export function jsonToData (json, expand = expandAll, path = [], type = 'value')
|
||||||
* @param {JSONData} data
|
* @param {JSONData} data
|
||||||
* @return {Object | Array | string | number | boolean | null} json
|
* @return {Object | Array | string | number | boolean | null} json
|
||||||
*/
|
*/
|
||||||
export function dataToJson (data) {
|
export function dataToJson (data: JSONData) {
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'Array':
|
case 'Array':
|
||||||
return data.items.map(dataToJson)
|
return data.items.map(item => dataToJson(item.value))
|
||||||
|
|
||||||
case 'Object':
|
case 'Object':
|
||||||
const object = {}
|
const object = {}
|
||||||
|
@ -87,7 +96,7 @@ export function dataToJson (data) {
|
||||||
* @return {Path} dataPath
|
* @return {Path} dataPath
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function toDataPath (data, path) {
|
export function toDataPath (data: JSONData, path: Path) : Path {
|
||||||
if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -95,14 +104,14 @@ export function toDataPath (data, path) {
|
||||||
if (data.type === 'Array') {
|
if (data.type === 'Array') {
|
||||||
// index of an array
|
// index of an array
|
||||||
const index = path[0]
|
const index = path[0]
|
||||||
const item = data.items[index]
|
const item = data.items[parseInt(index)]
|
||||||
if (!item) {
|
if (!item) {
|
||||||
throw new Error('Array item "' + index + '" not found')
|
throw new Error('Array item "' + index + '" not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['items', index].concat(toDataPath(item, path.slice(1)))
|
return ['items', index, 'value'].concat(toDataPath(item.value, path.slice(1)))
|
||||||
}
|
}
|
||||||
else {
|
else if (data.type === 'Object') {
|
||||||
// object property. find the index of this property
|
// object property. find the index of this property
|
||||||
const index = findPropertyIndex(data, path[0])
|
const index = findPropertyIndex(data, path[0])
|
||||||
const prop = data.props[index]
|
const prop = data.props[index]
|
||||||
|
@ -111,19 +120,11 @@ export function toDataPath (data, path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['props', index, 'value']
|
return ['props', index, 'value']
|
||||||
.concat(toDataPath(prop && prop.value, path.slice(1)))
|
.concat(toDataPath(prop.value, path.slice(1)))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a path of a JSON object into a path in the corresponding data model
|
|
||||||
* @param {JSONData} data
|
|
||||||
* @param {Path} path
|
|
||||||
* @return {Path} dataPath
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
export function toPath (data, dataPath) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +135,7 @@ export function toPath (data, dataPath) {
|
||||||
* what nodes must be expanded
|
* what nodes must be expanded
|
||||||
* @return {{data: JSONData, revert: Object[], error: Error | null}}
|
* @return {{data: JSONData, revert: Object[], error: Error | null}}
|
||||||
*/
|
*/
|
||||||
export function patchData (data, patch, expand = expandAll) {
|
export function patchData (data: JSONData, patch: JSONPatchAction[], expand = expandAll) {
|
||||||
let updatedData = data
|
let updatedData = data
|
||||||
let revert = []
|
let revert = []
|
||||||
|
|
||||||
|
@ -142,6 +143,8 @@ export function patchData (data, patch, expand = expandAll) {
|
||||||
const action = patch[i]
|
const action = patch[i]
|
||||||
const options = action.jsoneditor
|
const options = action.jsoneditor
|
||||||
|
|
||||||
|
// TODO: check whether action.op and action.path exist
|
||||||
|
|
||||||
switch (action.op) {
|
switch (action.op) {
|
||||||
case 'add': {
|
case 'add': {
|
||||||
const path = parseJSONPointer(action.path)
|
const path = parseJSONPointer(action.path)
|
||||||
|
@ -172,6 +175,14 @@ export function patchData (data, patch, expand = expandAll) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'copy': {
|
case 'copy': {
|
||||||
|
if (!action.from) {
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
revert: [],
|
||||||
|
error: new Error('Property "from" expected in copy action ' + JSON.stringify(action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = copy(updatedData, action.path, action.from, options)
|
const result = copy(updatedData, action.path, action.from, options)
|
||||||
updatedData = result.data
|
updatedData = result.data
|
||||||
revert = result.revert.concat(revert)
|
revert = result.revert.concat(revert)
|
||||||
|
@ -180,6 +191,14 @@ export function patchData (data, patch, expand = expandAll) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'move': {
|
case 'move': {
|
||||||
|
if (!action.from) {
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
revert: [],
|
||||||
|
error: new Error('Property "from" expected in move action ' + JSON.stringify(action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = move(updatedData, action.path, action.from, options)
|
const result = move(updatedData, action.path, action.from, options)
|
||||||
updatedData = result.data
|
updatedData = result.data
|
||||||
revert = result.revert.concat(revert)
|
revert = result.revert.concat(revert)
|
||||||
|
@ -224,7 +243,7 @@ export function patchData (data, patch, expand = expandAll) {
|
||||||
* @param {JSONData} value
|
* @param {JSONData} value
|
||||||
* @return {{data: JSONData, revert: JSONPatch}}
|
* @return {{data: JSONData, revert: JSONPatch}}
|
||||||
*/
|
*/
|
||||||
export function replace (data, path, value) {
|
export function replace (data: JSONData, path: Path, value: JSONData) {
|
||||||
const dataPath = toDataPath(data, path)
|
const dataPath = toDataPath(data, path)
|
||||||
const oldValue = getIn(data, dataPath)
|
const oldValue = getIn(data, dataPath)
|
||||||
|
|
||||||
|
@ -247,7 +266,7 @@ export function replace (data, path, value) {
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @return {{data: JSONData, revert: JSONPatch}}
|
* @return {{data: JSONData, revert: JSONPatch}}
|
||||||
*/
|
*/
|
||||||
export function remove (data, path) {
|
export function remove (data: JSONData, path: string) {
|
||||||
// console.log('remove', path)
|
// console.log('remove', path)
|
||||||
const pathArray = parseJSONPointer(path)
|
const pathArray = parseJSONPointer(path)
|
||||||
|
|
||||||
|
@ -259,6 +278,9 @@ export function remove (data, path) {
|
||||||
if (parent.type === 'Array') {
|
if (parent.type === 'Array') {
|
||||||
const dataPath = toDataPath(data, pathArray)
|
const dataPath = toDataPath(data, pathArray)
|
||||||
|
|
||||||
|
// remove the 'value' property, we want to remove the whole item from the items array
|
||||||
|
dataPath.pop()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: deleteIn(data, dataPath),
|
data: deleteIn(data, dataPath),
|
||||||
revert: [{
|
revert: [{
|
||||||
|
@ -275,7 +297,9 @@ export function remove (data, path) {
|
||||||
const dataPath = toDataPath(data, pathArray)
|
const dataPath = toDataPath(data, pathArray)
|
||||||
const prop = pathArray[pathArray.length - 1]
|
const prop = pathArray[pathArray.length - 1]
|
||||||
|
|
||||||
dataPath.pop() // remove the 'value' property, we want to remove the whole object property
|
// remove the 'value' property, we want to remove the whole object property from props
|
||||||
|
dataPath.pop()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: deleteIn(data, dataPath),
|
data: deleteIn(data, dataPath),
|
||||||
revert: [{
|
revert: [{
|
||||||
|
@ -298,12 +322,12 @@ export function remove (data, path) {
|
||||||
* @param {JSONPatch} patch
|
* @param {JSONPatch} patch
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function simplifyPatch(patch) {
|
export function simplifyPatch(patch: JSONPatch) {
|
||||||
const simplifiedPatch = []
|
const simplifiedPatch = []
|
||||||
const paths = {}
|
const paths = {}
|
||||||
|
|
||||||
// loop over the patch from last to first
|
// loop over the patch from last to first
|
||||||
for (var i = patch.length - 1; i >= 0; i--) {
|
for (let i = patch.length - 1; i >= 0; i--) {
|
||||||
const action = patch[i]
|
const action = patch[i]
|
||||||
if (action.op === 'test') {
|
if (action.op === 'test') {
|
||||||
// ignore test actions
|
// ignore test actions
|
||||||
|
@ -331,7 +355,7 @@ export function simplifyPatch(patch) {
|
||||||
* @return {{data: JSONData, revert: JSONPatch}}
|
* @return {{data: JSONData, revert: JSONPatch}}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function add (data, path, value, options) {
|
export function add (data: JSONData, path: string, value: JSONData, options) {
|
||||||
const pathArray = parseJSONPointer(path)
|
const pathArray = parseJSONPointer(path)
|
||||||
const parentPath = pathArray.slice(0, pathArray.length - 1)
|
const parentPath = pathArray.slice(0, pathArray.length - 1)
|
||||||
const dataPath = toDataPath(data, parentPath)
|
const dataPath = toDataPath(data, parentPath)
|
||||||
|
@ -341,16 +365,29 @@ export function add (data, path, value, options) {
|
||||||
|
|
||||||
let updatedData
|
let updatedData
|
||||||
if (parent.type === 'Array') {
|
if (parent.type === 'Array') {
|
||||||
updatedData = insertAt(data, dataPath.concat('items', prop), value)
|
const newItem = {
|
||||||
|
id: getId(), // TODO: create a unique id within current id's instead of using a global, ever incrementing id
|
||||||
|
value
|
||||||
|
}
|
||||||
|
updatedData = insertAt(data, dataPath.concat('items', prop), newItem)
|
||||||
}
|
}
|
||||||
else { // parent.type === 'Object'
|
else { // parent.type === 'Object'
|
||||||
updatedData = updateIn(data, dataPath, (object) => {
|
updatedData = updateIn(data, dataPath, (object) => {
|
||||||
const newProp = { name: prop, value }
|
const existingIndex = findPropertyIndex(object, prop)
|
||||||
const index = (options && typeof options.before === 'string')
|
if (existingIndex !== -1) {
|
||||||
? findPropertyIndex(object, options.before) // insert before
|
// replace existing item
|
||||||
: object.props.length // append
|
return setIn(object, ['props', existingIndex, 'value'], value)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// insert new item
|
||||||
|
const newId = getId()
|
||||||
|
const newProp = { id: newId, name: prop, value }
|
||||||
|
const index = (options && typeof options.before === 'string')
|
||||||
|
? findPropertyIndex(object, options.before) // insert before
|
||||||
|
: object.props.length // append
|
||||||
|
|
||||||
return insertAt(object, ['props', index], newProp)
|
return insertAt(object, ['props', index], newProp)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +424,7 @@ export function add (data, path, value, options) {
|
||||||
* @return {{data: JSONData, revert: JSONPatch}}
|
* @return {{data: JSONData, revert: JSONPatch}}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function copy (data, path, from, options) {
|
export function copy (data: JSONData, path: string, from: string, options) {
|
||||||
const value = getIn(data, toDataPath(data, parseJSONPointer(from)))
|
const value = getIn(data, toDataPath(data, parseJSONPointer(from)))
|
||||||
|
|
||||||
return add(data, path, value, options)
|
return add(data, path, value, options)
|
||||||
|
@ -402,7 +439,7 @@ export function copy (data, path, from, options) {
|
||||||
* @return {{data: JSONData, revert: JSONPatch}}
|
* @return {{data: JSONData, revert: JSONPatch}}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function move (data, path, from, options) {
|
export function move (data: JSONData, path: string, from: string, options) {
|
||||||
const fromArray = parseJSONPointer(from)
|
const fromArray = parseJSONPointer(from)
|
||||||
const dataValue = getIn(data, toDataPath(data, fromArray))
|
const dataValue = getIn(data, toDataPath(data, fromArray))
|
||||||
|
|
||||||
|
@ -446,7 +483,7 @@ export function move (data, path, from, options) {
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @return {null | Error} Returns an error when the tests, returns null otherwise
|
* @return {null | Error} Returns an error when the tests, returns null otherwise
|
||||||
*/
|
*/
|
||||||
export function test (data, path, value) {
|
export function test (data: JSONData, path: string, value: any) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return new Error('Test failed, no value provided')
|
return new Error('Test failed, no value provided')
|
||||||
}
|
}
|
||||||
|
@ -462,6 +499,8 @@ export function test (data, path, value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExpandCallback = (Path) => boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand or collapse one or multiple items or properties
|
* Expand or collapse one or multiple items or properties
|
||||||
* @param {JSONData} data
|
* @param {JSONData} data
|
||||||
|
@ -472,7 +511,7 @@ export function test (data, path, value) {
|
||||||
* @param {boolean} [expanded=true] New expanded state: true to expand, false to collapse
|
* @param {boolean} [expanded=true] New expanded state: true to expand, false to collapse
|
||||||
* @return {JSONData}
|
* @return {JSONData}
|
||||||
*/
|
*/
|
||||||
export function expand (data, callback, expanded: boolean = true) {
|
export function expand (data: JSONData, callback: Path | (Path) => boolean, expanded: boolean = true) {
|
||||||
// console.log('expand', callback, expand)
|
// console.log('expand', callback, expand)
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
|
@ -499,7 +538,7 @@ export function expand (data, callback, expanded: boolean = true) {
|
||||||
/**
|
/**
|
||||||
* Expand all Objects and Arrays on a path
|
* Expand all Objects and Arrays on a path
|
||||||
*/
|
*/
|
||||||
export function expandPath (data: JSONData, path: Path) {
|
export function expandPath (data: JSONData, path: Path) : JSONData {
|
||||||
let updatedData = data
|
let updatedData = data
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
|
@ -521,7 +560,7 @@ export function expandPath (data: JSONData, path: Path) {
|
||||||
* @param {JSONData} data
|
* @param {JSONData} data
|
||||||
* @param {JSONSchemaError[]} errors
|
* @param {JSONSchemaError[]} errors
|
||||||
*/
|
*/
|
||||||
export function addErrors (data, errors) {
|
export function addErrors (data: JSONData, errors) {
|
||||||
let updatedData = data
|
let updatedData = data
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
|
@ -641,24 +680,6 @@ export function addSearchResults (data: JSONData, searchResults: DataPointer[],
|
||||||
return updatedData
|
return updatedData
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge a object describing where the focus is to the data
|
|
||||||
*/
|
|
||||||
export function addFocus (data: JSONData, focusOn: DataPointer) {
|
|
||||||
if (focusOn.type == 'value') {
|
|
||||||
const dataPath = toDataPath(data, focusOn.path).concat('focus')
|
|
||||||
return setIn(data, dataPath, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (focusOn.type === 'property') {
|
|
||||||
const valueDataPath = toDataPath(data, focusOn.path)
|
|
||||||
const propertyDataPath = allButLast(valueDataPath).concat('focus')
|
|
||||||
return setIn(data, propertyDataPath, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do a case insensitive search for a search text in a text
|
* Do a case insensitive search for a search text in a text
|
||||||
* @param {String} text
|
* @param {String} text
|
||||||
|
@ -669,13 +690,15 @@ export function containsCaseInsensitive (text: string, search: string): boolean
|
||||||
return String(text).toLowerCase().indexOf(search.toLowerCase()) !== -1
|
return String(text).toLowerCase().indexOf(search.toLowerCase()) !== -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecurseCallback = (JSONData, Path, JSONData) => JSONData
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively transform JSONData: a recursive "map" function
|
* Recursively transform JSONData: a recursive "map" function
|
||||||
* @param {JSONData} data
|
* @param {JSONData} data
|
||||||
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
||||||
* @return {JSONData} Returns the transformed data
|
* @return {JSONData} Returns the transformed data
|
||||||
*/
|
*/
|
||||||
export function transform (data, callback) {
|
export function transform (data: JSONData, callback: RecurseCallback) {
|
||||||
return recurseTransform (data, [], data, callback)
|
return recurseTransform (data, [], data, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,40 +710,33 @@ export function transform (data, callback) {
|
||||||
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
||||||
* @return {JSONData} Returns the transformed data
|
* @return {JSONData} Returns the transformed data
|
||||||
*/
|
*/
|
||||||
function recurseTransform (value, path, root, callback) {
|
function recurseTransform (value: JSONData, path: Path, root?: JSONData, callback: RecurseCallback) : JSONData{
|
||||||
let updatedValue = callback(value, path, root)
|
let updatedValue = callback(value, path, root)
|
||||||
|
|
||||||
switch (value.type) {
|
if (value.type === 'Array') {
|
||||||
case 'Array': {
|
let updatedItems = updatedValue.items
|
||||||
let updatedItems = updatedValue.items
|
|
||||||
|
|
||||||
updatedValue.items.forEach((item, index) => {
|
updatedValue.items.forEach((item, index) => {
|
||||||
const updatedItem = recurseTransform(item, path.concat(String(index)), root, callback)
|
const updatedItem = recurseTransform(item.value, path.concat(String(index)), root, callback)
|
||||||
updatedItems = setIn(updatedItems, [index], updatedItem)
|
updatedItems = setIn(updatedItems, [index, 'value'], updatedItem)
|
||||||
})
|
})
|
||||||
|
|
||||||
updatedValue = setIn(updatedValue, ['items'], updatedItems)
|
updatedValue = setIn(updatedValue, ['items'], updatedItems)
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Object': {
|
|
||||||
let updatedProps = updatedValue.props
|
|
||||||
|
|
||||||
updatedValue.props.forEach((prop, index) => {
|
|
||||||
const updatedItem = recurseTransform(prop.value, path.concat(prop.name), root, callback)
|
|
||||||
updatedProps = setIn(updatedProps, [index, 'value'], updatedItem)
|
|
||||||
})
|
|
||||||
|
|
||||||
updatedValue = setIn(updatedValue, ['props'], updatedProps)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // type 'string' or 'value'
|
|
||||||
// no childs to traverse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.type === 'Object') {
|
||||||
|
let updatedProps = updatedValue.props
|
||||||
|
|
||||||
|
updatedValue.props.forEach((prop, index) => {
|
||||||
|
const updatedItem = recurseTransform(prop.value, path.concat(prop.name), root, callback)
|
||||||
|
updatedProps = setIn(updatedProps, [index, 'value'], updatedItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
updatedValue = setIn(updatedValue, ['props'], updatedProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
// (for type 'string' or 'value' there are no childs to traverse)
|
||||||
|
|
||||||
return updatedValue
|
return updatedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,7 +745,7 @@ function recurseTransform (value, path, root, callback) {
|
||||||
* @param {JSONData} data
|
* @param {JSONData} data
|
||||||
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
||||||
*/
|
*/
|
||||||
export function traverse (data, callback) {
|
export function traverse (data: JSONData, callback: RecurseCallback) {
|
||||||
return recurseTraverse (data, [], data, callback)
|
return recurseTraverse (data, [], data, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,13 +756,13 @@ export function traverse (data, callback) {
|
||||||
* @param {JSONData | null} root The root object, object at path=[]
|
* @param {JSONData | null} root The root object, object at path=[]
|
||||||
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
* @param {function(value: JSONData, path: Path, root: JSONData)} callback
|
||||||
*/
|
*/
|
||||||
function recurseTraverse (value, path, root, callback) {
|
function recurseTraverse (value: JSONData, path: Path, root: JSONData, callback: RecurseCallback) {
|
||||||
callback(value, path, root)
|
callback(value, path, root)
|
||||||
|
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
case 'Array': {
|
case 'Array': {
|
||||||
value.items.forEach((item, index) => {
|
value.items.forEach((item: ItemData, index) => {
|
||||||
recurseTraverse(item, path.concat(String(index)), root, callback)
|
recurseTraverse(item.value, path.concat(String(index)), root, callback)
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -783,7 +799,9 @@ export function pathExists (data, path) {
|
||||||
if (data.type === 'Array') {
|
if (data.type === 'Array') {
|
||||||
// index of an array
|
// index of an array
|
||||||
index = path[0]
|
index = path[0]
|
||||||
return pathExists(data.items[index], path.slice(1))
|
const item = data.items[index]
|
||||||
|
|
||||||
|
return pathExists(item && item.value, path.slice(1))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// object property. find the index of this property
|
// object property. find the index of this property
|
||||||
|
@ -825,7 +843,7 @@ export function resolvePathIndex (data, path) {
|
||||||
* @return {string | null} Returns the name of the next property,
|
* @return {string | null} Returns the name of the next property,
|
||||||
* or null if there is none
|
* or null if there is none
|
||||||
*/
|
*/
|
||||||
export function findNextProp (parent, prop) {
|
export function findNextProp (parent: ObjectData, prop: string) : string | null {
|
||||||
const index = findPropertyIndex(parent, prop)
|
const index = findPropertyIndex(parent, prop)
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return null
|
return null
|
||||||
|
@ -841,7 +859,7 @@ export function findNextProp (parent, prop) {
|
||||||
* @param {string} prop
|
* @param {string} prop
|
||||||
* @return {number} Returns the index when found, -1 when not found
|
* @return {number} Returns the index when found, -1 when not found
|
||||||
*/
|
*/
|
||||||
export function findPropertyIndex (object, prop) {
|
export function findPropertyIndex (object: ObjectData, prop: string) {
|
||||||
return object.props.findIndex(p => p.name === prop)
|
return object.props.findIndex(p => p.name === prop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,7 +869,7 @@ export function findPropertyIndex (object, prop) {
|
||||||
* @param {string} pointer
|
* @param {string} pointer
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function parseJSONPointer (pointer) {
|
export function parseJSONPointer (pointer: string) {
|
||||||
const path = pointer.split('/')
|
const path = pointer.split('/')
|
||||||
path.shift() // remove the first empty entry
|
path.shift() // remove the first empty entry
|
||||||
|
|
||||||
|
@ -864,12 +882,33 @@ export function parseJSONPointer (pointer) {
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
export function compileJSONPointer (path) {
|
export function compileJSONPointer (path: Path) {
|
||||||
return path
|
return path
|
||||||
.map(p => '/' + String(p).replace(/~/g, '~0').replace(/\//g, '~1'))
|
.map(p => '/' + String(p).replace(/~/g, '~0').replace(/\//g, '~1'))
|
||||||
.join('')
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a new "unique" id. Id's are created from an incremental counter.
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
// TODO: use createUniqueId instead of getId()
|
||||||
|
function getId () : number {
|
||||||
|
_id++
|
||||||
|
return _id
|
||||||
|
}
|
||||||
|
let _id = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a unique id from an array with properties each having an id field.
|
||||||
|
* The
|
||||||
|
* @param {{id: string}} array
|
||||||
|
*/
|
||||||
|
// TODO: use createUniqueId instead of getId()
|
||||||
|
function createUniqueId (array) {
|
||||||
|
return Math.max(...array.map(item => item.id)) + 1
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the last item of an array
|
* Returns the last item of an array
|
||||||
*/
|
*/
|
||||||
|
|
48
src/types.js
48
src/types.js
|
@ -1,19 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @typedef {'Object' | 'Array' | 'value' | 'string'} JSONDataType
|
|
||||||
*
|
|
||||||
* @typedef {{
|
|
||||||
* patch: JSONPatch,
|
|
||||||
* revert: JSONPatch,
|
|
||||||
* error: null | Error
|
|
||||||
* }} JSONPatchResult
|
|
||||||
*
|
|
||||||
* @typedef {{
|
|
||||||
* dataPath: string,
|
|
||||||
* message: string
|
|
||||||
* }} JSONSchemaError
|
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* name: string?,
|
* name: string?,
|
||||||
|
@ -33,10 +20,6 @@
|
||||||
* ace: Object?
|
* ace: Object?
|
||||||
* }} Options
|
* }} Options
|
||||||
*
|
*
|
||||||
* @typedef {{
|
|
||||||
* expand: function (path: Path)?
|
|
||||||
* }} PatchOptions
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,11 +36,17 @@ export type SearchResultStatus = 'normal' | 'active'
|
||||||
export type DataPointerType = 'value' | 'property'
|
export type DataPointerType = 'value' | 'property'
|
||||||
|
|
||||||
export type PropertyData = {
|
export type PropertyData = {
|
||||||
|
id: number,
|
||||||
name: string,
|
name: string,
|
||||||
value: JSONData,
|
value: JSONData,
|
||||||
searchResult: ?SearchResultStatus
|
searchResult: ?SearchResultStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ItemData = {
|
||||||
|
id: number,
|
||||||
|
value: JSONData
|
||||||
|
}
|
||||||
|
|
||||||
export type ObjectData = {
|
export type ObjectData = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: ?boolean,
|
expanded: ?boolean,
|
||||||
|
@ -67,7 +56,7 @@ export type ObjectData = {
|
||||||
export type ArrayData = {
|
export type ArrayData = {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: ?boolean,
|
expanded: ?boolean,
|
||||||
items: ?JSONData[]
|
items: ItemData[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ValueData = {
|
export type ValueData = {
|
||||||
|
@ -78,6 +67,8 @@ export type ValueData = {
|
||||||
|
|
||||||
export type JSONData = ObjectData | ArrayData | ValueData
|
export type JSONData = ObjectData | ArrayData | ValueData
|
||||||
|
|
||||||
|
export type JSONDataType = 'Object' | 'Array' | 'value' | 'string'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type Path = string[]
|
export type Path = string[]
|
||||||
|
@ -96,8 +87,25 @@ export type SetOptions = {
|
||||||
|
|
||||||
export type JSONPatchAction = {
|
export type JSONPatchAction = {
|
||||||
op: string, // TODO: define allowed ops
|
op: string, // TODO: define allowed ops
|
||||||
path?: string,
|
path: string,
|
||||||
from?: string,
|
from?: string,
|
||||||
value?: any
|
value?: any,
|
||||||
|
jsoneditor?: PatchOptions
|
||||||
}
|
}
|
||||||
export type JSONPatch = JSONPatchAction[]
|
export type JSONPatch = JSONPatchAction[]
|
||||||
|
|
||||||
|
export type PatchOptions = {
|
||||||
|
type: JSONDataType,
|
||||||
|
expand: (Path) => boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JSONPatchResult = {
|
||||||
|
patch: JSONPatch,
|
||||||
|
revert: JSONPatch,
|
||||||
|
error: null | Error
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JSONSchemaError = {
|
||||||
|
dataPath: string,
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
|
@ -20,45 +20,58 @@ const JSON_DATA_EXAMPLE = {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'obj',
|
name: 'obj',
|
||||||
value: {
|
value: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'arr',
|
name: 'arr',
|
||||||
value: {
|
value: {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 1
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 2
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Object',
|
id: '[ID]',
|
||||||
expanded: true,
|
value: {
|
||||||
props: [
|
type: 'Object',
|
||||||
{
|
expanded: true,
|
||||||
name: 'first',
|
props: [
|
||||||
value: {
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 3
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
name: 'last',
|
}
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
value: 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +79,7 @@ const JSON_DATA_EXAMPLE = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'str',
|
name: 'str',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -73,6 +87,7 @@ const JSON_DATA_EXAMPLE = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'nill',
|
name: 'nill',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -80,6 +95,7 @@ const JSON_DATA_EXAMPLE = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'bool',
|
name: 'bool',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -89,50 +105,66 @@ const JSON_DATA_EXAMPLE = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: instead of all slightly different copies of JSON_DATA_EXAMPLE, built them up via setIn, updateIn based on JSON_DATA_EXAMPLE
|
||||||
|
|
||||||
const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'obj',
|
name: 'obj',
|
||||||
value: {
|
value: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'arr',
|
name: 'arr',
|
||||||
value: {
|
value: {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
items: [
|
items: [
|
||||||
|
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 1
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 2
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Object',
|
id: '[ID]',
|
||||||
expanded: false,
|
value: {
|
||||||
props: [
|
type: 'Object',
|
||||||
{
|
expanded: false,
|
||||||
name: 'first',
|
props: [
|
||||||
value: {
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 3
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
name: 'last',
|
}
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
value: 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +172,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'str',
|
name: 'str',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -147,6 +180,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'nill',
|
name: 'nill',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -154,6 +188,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_1 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'bool',
|
name: 'bool',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -168,45 +203,58 @@ const JSON_DATA_EXAMPLE_COLLAPSED_2 = {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'obj',
|
name: 'obj',
|
||||||
value: {
|
value: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: false,
|
expanded: false,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'arr',
|
name: 'arr',
|
||||||
value: {
|
value: {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: false,
|
expanded: false,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 1
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 2
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Object',
|
id: '[ID]',
|
||||||
expanded: false,
|
value: {
|
||||||
props: [
|
type: 'Object',
|
||||||
{
|
expanded: false,
|
||||||
name: 'first',
|
props: [
|
||||||
value: {
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 3
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
name: 'last',
|
}
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
value: 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +262,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_2 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'str',
|
name: 'str',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -221,6 +270,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_2 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'nill',
|
name: 'nill',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -228,6 +278,7 @@ const JSON_DATA_EXAMPLE_COLLAPSED_2 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'bool',
|
name: 'bool',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -243,45 +294,58 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'obj',
|
name: 'obj',
|
||||||
value: {
|
value: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'arr',
|
name: 'arr',
|
||||||
value: {
|
value: {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 1
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 2
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Object',
|
id: '[ID]',
|
||||||
expanded: true,
|
value: {
|
||||||
props: [
|
type: 'Object',
|
||||||
{
|
expanded: true,
|
||||||
name: 'first',
|
props: [
|
||||||
value: {
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 3
|
name: 'first',
|
||||||
}
|
value: {
|
||||||
},
|
type: 'value',
|
||||||
{
|
value: 3
|
||||||
name: 'last',
|
}
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
value: 4
|
|
||||||
},
|
},
|
||||||
searchResult: 'active'
|
{
|
||||||
}
|
id: '[ID]',
|
||||||
]
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4
|
||||||
|
},
|
||||||
|
searchResult: 'active'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -290,6 +354,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'str',
|
name: 'str',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -298,6 +363,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'nill',
|
name: 'nill',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -307,6 +373,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
|
||||||
searchResult: 'normal'
|
searchResult: 'normal'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'bool',
|
name: 'bool',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -322,11 +389,13 @@ const JSON_DATA_SMALL = {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'obj',
|
name: 'obj',
|
||||||
value: {
|
value: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'a',
|
name: 'a',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -337,13 +406,17 @@ const JSON_DATA_SMALL = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'arr',
|
name: 'arr',
|
||||||
value: {
|
value: {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 3
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -362,46 +435,59 @@ const JSON_DATA_EXAMPLE_ERRORS = {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'obj',
|
name: 'obj',
|
||||||
value: {
|
value: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
props: [
|
props: [
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'arr',
|
name: 'arr',
|
||||||
value: {
|
value: {
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
expanded: true,
|
expanded: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 1
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 2
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Object',
|
id: '[ID]',
|
||||||
expanded: true,
|
value: {
|
||||||
props: [
|
type: 'Object',
|
||||||
{
|
expanded: true,
|
||||||
name: 'first',
|
props: [
|
||||||
value: {
|
{
|
||||||
type: 'value',
|
id: '[ID]',
|
||||||
value: 3
|
name: 'first',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '[ID]',
|
||||||
|
name: 'last',
|
||||||
|
value: {
|
||||||
|
type: 'value',
|
||||||
|
value: 4,
|
||||||
|
error: JSON_SCHEMA_ERRORS[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
name: 'last',
|
}
|
||||||
value: {
|
|
||||||
type: 'value',
|
|
||||||
value: 4,
|
|
||||||
error: JSON_SCHEMA_ERRORS[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,6 +495,7 @@ const JSON_DATA_EXAMPLE_ERRORS = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'str',
|
name: 'str',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -416,6 +503,7 @@ const JSON_DATA_EXAMPLE_ERRORS = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'nill',
|
name: 'nill',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -424,6 +512,7 @@ const JSON_DATA_EXAMPLE_ERRORS = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: '[ID]',
|
||||||
name: 'bool',
|
name: 'bool',
|
||||||
value: {
|
value: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
@ -438,7 +527,10 @@ test('jsonToData', t => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
t.deepEqual(jsonToData(JSON_EXAMPLE, expand, []), JSON_DATA_EXAMPLE)
|
const jsonData = jsonToData(JSON_EXAMPLE, expand, [])
|
||||||
|
replaceIds(jsonData)
|
||||||
|
|
||||||
|
t.deepEqual(jsonData, JSON_DATA_EXAMPLE)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('dataToJson', t => {
|
test('dataToJson', t => {
|
||||||
|
@ -622,6 +714,21 @@ test('jsonpatch replace', t => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('jsonpatch replace (keep ids intact)', t => {
|
||||||
|
const json = { value: 42 }
|
||||||
|
const patch = [
|
||||||
|
{op: 'replace', path: '/value', value: 100}
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = jsonToData(json)
|
||||||
|
const valueId = data.props[0].id
|
||||||
|
|
||||||
|
const patchedData = patchData(data, patch).data
|
||||||
|
const patchedValueId = patchedData.props[0].id
|
||||||
|
|
||||||
|
t.is(patchedValueId, valueId)
|
||||||
|
})
|
||||||
|
|
||||||
test('jsonpatch copy', t => {
|
test('jsonpatch copy', t => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1,2,3],
|
||||||
|
@ -659,6 +766,34 @@ test('jsonpatch copy', t => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// test('jsonpatch copy (create new ids)', t => {
|
||||||
|
// const json = { foo: { bar: 42 } }
|
||||||
|
// const patch = [
|
||||||
|
// {op: 'copy', from: '/foo', path: '/copied'}
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
// const data = jsonToData(json)
|
||||||
|
// const objectId = data.id
|
||||||
|
// const fooId = data.props[0].value.id
|
||||||
|
// const barId = data.props[0].value.props[0].value.id
|
||||||
|
//
|
||||||
|
// const patchedData = patchData(data, patch).data
|
||||||
|
// const patchedObjectId = patchedData.id
|
||||||
|
// const patchedFooId = patchedData.props[0].value.id
|
||||||
|
// const patchedBarId = patchedData.props[0].value.props[0].value.id
|
||||||
|
// const patchedCopiedId = patchedData.props[1].value.id
|
||||||
|
// const patchedCopiedBarId = patchedData.props[1].value.props[0].value.id
|
||||||
|
//
|
||||||
|
// t.is(patchedData.props[0].name, 'foo')
|
||||||
|
// t.is(patchedData.props[1].name, 'copied')
|
||||||
|
//
|
||||||
|
// t.is(patchedObjectId, objectId, 'same object id')
|
||||||
|
// t.is(patchedFooId, fooId, 'same foo id')
|
||||||
|
// t.is(patchedBarId, barId, 'same bar id')
|
||||||
|
// t.not(patchedCopiedId, patchedFooId, 'different copied foo id')
|
||||||
|
// t.not(patchedCopiedBarId, patchedBarId, 'different copied bar id')
|
||||||
|
// })
|
||||||
|
//
|
||||||
test('jsonpatch move', t => {
|
test('jsonpatch move', t => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1,2,3],
|
||||||
|
@ -732,6 +867,54 @@ test('jsonpatch move before', t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('jsonpatch move and replace', t => {
|
test('jsonpatch move and replace', t => {
|
||||||
|
const json = { a: 2, b: 3 }
|
||||||
|
|
||||||
|
const patch = [
|
||||||
|
{op: 'move', from: '/a', path: '/b'},
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = jsonToData(json)
|
||||||
|
const result = patchData(data, patch)
|
||||||
|
const patchedData = result.data
|
||||||
|
const revert = result.revert
|
||||||
|
const patchedJson = dataToJson(patchedData)
|
||||||
|
|
||||||
|
replaceIds(patchedData)
|
||||||
|
t.deepEqual(patchedData, {
|
||||||
|
"type": "Object",
|
||||||
|
"expanded": true,
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"id": "[ID]",
|
||||||
|
"name": "b",
|
||||||
|
"value": {
|
||||||
|
"type": "value",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
t.deepEqual(patchedJson, { b : 2 })
|
||||||
|
t.deepEqual(revert, [
|
||||||
|
{op:'move', from: '/b', path: '/a'},
|
||||||
|
{op:'add', path:'/b', value: 3, jsoneditor: {type: 'value', before: 'b'}}
|
||||||
|
])
|
||||||
|
|
||||||
|
// test revert
|
||||||
|
const data2 = jsonToData(patchedJson)
|
||||||
|
const result2 = patchData(data2, revert)
|
||||||
|
const patchedData2 = result2.data
|
||||||
|
const revert2 = result2.revert
|
||||||
|
const patchedJson2 = dataToJson(patchedData2)
|
||||||
|
|
||||||
|
t.deepEqual(patchedJson2, json)
|
||||||
|
t.deepEqual(revert2, [
|
||||||
|
{op: 'move', from: '/a', path: '/b'}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('jsonpatch move and replace (nested)', t => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1,2,3],
|
||||||
obj: {a : 4}
|
obj: {a : 4}
|
||||||
|
@ -768,6 +951,44 @@ test('jsonpatch move and replace', t => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('jsonpatch move (keep id intact)', t => {
|
||||||
|
const json = { value: 42 }
|
||||||
|
const patch = [
|
||||||
|
{op: 'move', from: '/value', path: '/moved'}
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = jsonToData(json)
|
||||||
|
const objectId = data.id
|
||||||
|
const valueId = data.props[0].value.id
|
||||||
|
|
||||||
|
const patchedData = patchData(data, patch).data
|
||||||
|
|
||||||
|
const patchedObjectId = patchedData.id
|
||||||
|
const patchedValueId = patchedData.props[0].value.id
|
||||||
|
|
||||||
|
t.is(patchedObjectId, objectId)
|
||||||
|
t.is(patchedValueId, valueId)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('jsonpatch move and replace (keep ids intact)', t => {
|
||||||
|
const json = { a: 2, b: 3 }
|
||||||
|
const patch = [
|
||||||
|
{op: 'move', from: '/a', path: '/b'}
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = jsonToData(json)
|
||||||
|
const bId = data.props[1].id
|
||||||
|
|
||||||
|
t.is(data.props[0].name, 'a')
|
||||||
|
t.is(data.props[1].name, 'b')
|
||||||
|
|
||||||
|
const patchedData = patchData(data, patch).data
|
||||||
|
const patchedBId = patchedData.props[0].id
|
||||||
|
|
||||||
|
t.is(patchedData.props[0].name, 'b')
|
||||||
|
t.is(patchedBId, bId)
|
||||||
|
})
|
||||||
|
|
||||||
test('jsonpatch test (ok)', t => {
|
test('jsonpatch test (ok)', t => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1,2,3],
|
||||||
|
@ -855,7 +1076,7 @@ test('add and remove errors', t => {
|
||||||
|
|
||||||
test('transform', t => {
|
test('transform', t => {
|
||||||
// {obj: {a: 2}, arr: [3]}
|
// {obj: {a: 2}, arr: [3]}
|
||||||
|
|
||||||
let log = []
|
let log = []
|
||||||
const transformed = transform(JSON_DATA_SMALL, function (value, path, root) {
|
const transformed = transform(JSON_DATA_SMALL, function (value, path, root) {
|
||||||
t.is(root, JSON_DATA_SMALL)
|
t.is(root, JSON_DATA_SMALL)
|
||||||
|
@ -878,18 +1099,18 @@ test('transform', t => {
|
||||||
[JSON_DATA_SMALL.props[0].value, ['obj'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[0].value, ['obj'], JSON_DATA_SMALL],
|
||||||
[JSON_DATA_SMALL.props[0].value.props[0].value, ['obj', 'a'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[0].value.props[0].value, ['obj', 'a'], JSON_DATA_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value, ['arr'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[1].value, ['arr'], JSON_DATA_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value.items[0], ['arr', '0'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[1].value.items[0].value, ['arr', '0'], JSON_DATA_SMALL],
|
||||||
]
|
]
|
||||||
|
|
||||||
// log.forEach((row, index) => {
|
log.forEach((row, index) => {
|
||||||
// t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
||||||
// })
|
})
|
||||||
t.deepEqual(log, EXPECTED_LOG)
|
t.deepEqual(log, EXPECTED_LOG)
|
||||||
t.not(transformed, JSON_DATA_SMALL)
|
t.not(transformed, JSON_DATA_SMALL)
|
||||||
t.not(transformed.props[0].value, JSON_DATA_SMALL.props[0].value)
|
t.not(transformed.props[0].value, JSON_DATA_SMALL.props[0].value)
|
||||||
t.not(transformed.props[0].value.props[0].value, JSON_DATA_SMALL.props[0].value.props[0].value)
|
t.not(transformed.props[0].value.props[0].value, JSON_DATA_SMALL.props[0].value.props[0].value)
|
||||||
t.is(transformed.props[1].value, JSON_DATA_SMALL.props[1].value)
|
t.is(transformed.props[1].value, JSON_DATA_SMALL.props[1].value)
|
||||||
t.is(transformed.props[1].value.items[0], JSON_DATA_SMALL.props[1].value.items[0])
|
t.is(transformed.props[1].value.items[0].value, JSON_DATA_SMALL.props[1].value.items[0].value)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -910,19 +1131,19 @@ test('traverse', t => {
|
||||||
[JSON_DATA_SMALL.props[0].value, ['obj'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[0].value, ['obj'], JSON_DATA_SMALL],
|
||||||
[JSON_DATA_SMALL.props[0].value.props[0].value, ['obj', 'a'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[0].value.props[0].value, ['obj', 'a'], JSON_DATA_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value, ['arr'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[1].value, ['arr'], JSON_DATA_SMALL],
|
||||||
[JSON_DATA_SMALL.props[1].value.items[0], ['arr', '0'], JSON_DATA_SMALL],
|
[JSON_DATA_SMALL.props[1].value.items[0].value, ['arr', '0'], JSON_DATA_SMALL],
|
||||||
]
|
]
|
||||||
|
|
||||||
// log.forEach((row, index) => {
|
log.forEach((row, index) => {
|
||||||
// t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
t.deepEqual(log[index], EXPECTED_LOG[index], 'should have equal log at index ' + index )
|
||||||
// })
|
})
|
||||||
t.deepEqual(log, EXPECTED_LOG)
|
t.deepEqual(log, EXPECTED_LOG)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
test('search', t => {
|
test('search', t => {
|
||||||
const searchResults = search(JSON_DATA_EXAMPLE, 'L')
|
const searchResults = search(JSON_DATA_EXAMPLE, 'L')
|
||||||
//console.log(JSON.stringify(searchResults, null, 2))
|
// printJSON(searchResults)
|
||||||
|
|
||||||
t.deepEqual(searchResults, [
|
t.deepEqual(searchResults, [
|
||||||
{path: ['obj', 'arr', '2', 'last'], type: 'property'},
|
{path: ['obj', 'arr', '2', 'last'], type: 'property'},
|
||||||
|
@ -935,7 +1156,7 @@ test('search', t => {
|
||||||
|
|
||||||
const activeSearchResult = searchResults[0]
|
const activeSearchResult = searchResults[0]
|
||||||
const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults, activeSearchResult)
|
const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults, activeSearchResult)
|
||||||
// console.log(JSON.stringify(updatedData, null, 2))
|
// printJSON(updatedData)
|
||||||
|
|
||||||
t.deepEqual(updatedData, JSON_DATA_EXAMPLE_SEARCH_L)
|
t.deepEqual(updatedData, JSON_DATA_EXAMPLE_SEARCH_L)
|
||||||
})
|
})
|
||||||
|
@ -995,3 +1216,28 @@ test('previousSearchResult', t => {
|
||||||
// return null when searchResults are empty
|
// return null when searchResults are empty
|
||||||
t.deepEqual(previousSearchResult([], {path: ['non', 'existing'], type: 'value'}), null)
|
t.deepEqual(previousSearchResult([], {path: ['non', 'existing'], type: 'value'}), null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// helper function to replace all id properties with a constant value
|
||||||
|
function replaceIds (data, value = '[ID]') {
|
||||||
|
if (data.type === 'Object') {
|
||||||
|
data.props.forEach(prop => {
|
||||||
|
prop.id = value
|
||||||
|
replaceIds(prop.value, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type === 'Array') {
|
||||||
|
data.items.forEach(item => {
|
||||||
|
item.id = value
|
||||||
|
replaceIds(item.value, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to print JSON in the console
|
||||||
|
function printJSON (json, message = null) {
|
||||||
|
if (message) {
|
||||||
|
console.log(message)
|
||||||
|
}
|
||||||
|
console.log(JSON.stringify(json, null, 2))
|
||||||
|
}
|
Loading…
Reference in New Issue