Linting fixes

This commit is contained in:
Jos de Jong 2020-09-02 11:39:50 +02:00
parent d3c3d47ec0
commit 023c57f492
24 changed files with 360 additions and 386 deletions

View File

@ -26,6 +26,6 @@ export const SIMPLE_MODAL_OPTIONS = {
}, },
styleContent: { styleContent: {
padding: '0px', padding: '0px',
overflow: 'visible', // needed for select box dropdowns which are larger than the modal overflow: 'visible' // needed for select box dropdowns which are larger than the modal
} }
} }

View File

@ -198,7 +198,7 @@ export function patchProps (state, operations) {
return updatedState return updatedState
} }
export function getNextKeys(props, key, includeKey = false) { export function getNextKeys (props, key, includeKey = false) {
if (props) { if (props) {
const index = props.findIndex(prop => prop.key === key) const index = props.findIndex(prop => prop.key === key)
if (index !== -1) { if (index !== -1) {

View File

@ -10,8 +10,8 @@ import { syncState, updateProps } from './documentState.js'
describe('documentState', () => { describe('documentState', () => {
it('syncState', () => { it('syncState', () => {
const document = { const document = {
array: [1, 2, {c: 6}], array: [1, 2, { c: 6 }],
object: {a: 4, b: 5}, object: { a: 4, b: 5 },
value: 'hello' value: 'hello'
} }
@ -23,10 +23,10 @@ describe('documentState', () => {
const expectedState = {} const expectedState = {}
expectedState[STATE_EXPANDED] = true expectedState[STATE_EXPANDED] = true
expectedState[STATE_PROPS] = [ expectedState[STATE_PROPS] = [
{ 'id': state[STATE_PROPS][0].id, 'key': 'array' }, { id: state[STATE_PROPS][0].id, key: 'array' },
{ 'id': state[STATE_PROPS][1].id, 'key': 'object' }, { id: state[STATE_PROPS][1].id, key: 'object' },
{ 'id': state[STATE_PROPS][2].id, 'key': 'value' } { id: state[STATE_PROPS][2].id, key: 'value' }
] ]
expectedState.array = [] expectedState.array = []
expectedState.array[STATE_EXPANDED] = true expectedState.array[STATE_EXPANDED] = true
@ -34,30 +34,30 @@ describe('documentState', () => {
expectedState.array[2] = {} expectedState.array[2] = {}
expectedState.array[2][STATE_EXPANDED] = false expectedState.array[2][STATE_EXPANDED] = false
expectedState.array[2][STATE_PROPS] = [ expectedState.array[2][STATE_PROPS] = [
{ 'id': state.array[2][STATE_PROPS][0].id, 'key': 'c' } // FIXME: props should not be created because node is not expanded { id: state.array[2][STATE_PROPS][0].id, key: 'c' } // FIXME: props should not be created because node is not expanded
] ]
expectedState.object = {} expectedState.object = {}
expectedState.object[STATE_EXPANDED] = true expectedState.object[STATE_EXPANDED] = true
expectedState.object[STATE_PROPS] = [ expectedState.object[STATE_PROPS] = [
{ 'id': state.object[STATE_PROPS][0].id, 'key': 'a' }, { id: state.object[STATE_PROPS][0].id, key: 'a' },
{ 'id': state.object[STATE_PROPS][1].id, 'key': 'b' } { id: state.object[STATE_PROPS][1].id, key: 'b' }
] ]
assert.deepStrictEqual(state, expectedState) assert.deepStrictEqual(state, expectedState)
}) })
it('updateProps (1)', () => { it('updateProps (1)', () => {
const props1 = updateProps({b: 2}) const props1 = updateProps({ b: 2 })
assert.deepStrictEqual(props1.map(item => item.key), ['b']) assert.deepStrictEqual(props1.map(item => item.key), ['b'])
const props2 = updateProps({a: 1, b: 2}, props1) const props2 = updateProps({ a: 1, b: 2 }, props1)
assert.deepStrictEqual(props2.map(item => item.key), ['b', 'a']) assert.deepStrictEqual(props2.map(item => item.key), ['b', 'a'])
assert.deepStrictEqual(props2[0], props1[0]) // b must still have the same id assert.deepStrictEqual(props2[0], props1[0]) // b must still have the same id
}) })
it('updateProps (2)', () => { it('updateProps (2)', () => {
const props1 = updateProps({a: 1, b: 2}) const props1 = updateProps({ a: 1, b: 2 })
const props2 = updateProps({a: 1, b: 2}, props1) const props2 = updateProps({ a: 1, b: 2 }, props1)
assert.deepStrictEqual(props2, props1) assert.deepStrictEqual(props2, props1)
}) })

View File

@ -113,4 +113,4 @@ export function createHistory (options = {}) {
undo, undo,
redo redo
} }
} }

View File

@ -22,7 +22,7 @@ import { isObjectOrArray } from '../utils/typeUtils.js'
* key will maintain it's position above these keys * key will maintain it's position above these keys
* @return {JSONPatchDocument} * @return {JSONPatchDocument}
*/ */
export function insertBefore (json, path, values, nextKeys) { // TODO: find a better name and define datastructure for values export function insertBefore (json, path, values, nextKeys) { // TODO: find a better name and define datastructure for values
const parentPath = initial(path) const parentPath = initial(path)
const parent = getIn(json, parentPath) const parent = getIn(json, parentPath)
@ -33,8 +33,7 @@ export function insertBefore (json, path, values, nextKeys) { // TODO: find a b
path: compileJSONPointer(parentPath.concat(offset + index)), path: compileJSONPointer(parentPath.concat(offset + index)),
value: entry.value value: entry.value
})) }))
} } else { // 'object'
else { // 'object'
return [ return [
// insert new values // insert new values
...values.map(entry => { ...values.map(entry => {
@ -65,7 +64,7 @@ export function insertBefore (json, path, values, nextKeys) { // TODO: find a b
* @param {Array.<{key?: string, value: JSON}>} values * @param {Array.<{key?: string, value: JSON}>} values
* @return {JSONPatchDocument} * @return {JSONPatchDocument}
*/ */
export function append (json, path, values) { // TODO: find a better name and define datastructure for values export function append (json, path, values) { // TODO: find a better name and define datastructure for values
const parent = getIn(json, path) const parent = getIn(json, path)
if (Array.isArray(parent)) { if (Array.isArray(parent)) {
@ -75,8 +74,7 @@ export function append (json, path, values) { // TODO: find a better name and d
path: compileJSONPointer(path.concat(offset + index)), path: compileJSONPointer(path.concat(offset + index)),
value: entry.value value: entry.value
})) }))
} } else { // 'object'
else { // 'object'
return values.map(entry => { return values.map(entry => {
const newProp = findUniqueName(entry.key, parent) const newProp = findUniqueName(entry.key, parent)
return { return {
@ -100,7 +98,7 @@ export function append (json, path, values) { // TODO: find a better name and d
* key will maintain it's position above these keys * key will maintain it's position above these keys
* @returns {JSONPatchDocument} * @returns {JSONPatchDocument}
*/ */
export function rename(parentPath, oldKey, newKey, nextKeys) { export function rename (parentPath, oldKey, newKey, nextKeys) {
return [ return [
// rename a key // rename a key
{ {
@ -129,7 +127,7 @@ export function rename(parentPath, oldKey, newKey, nextKeys) {
* key will maintain it's position above these keys * key will maintain it's position above these keys
* @return {JSONPatchDocument} * @return {JSONPatchDocument}
*/ */
export function replace (json, paths, values, nextKeys) { // TODO: find a better name and define datastructure for values export function replace (json, paths, values, nextKeys) { // TODO: find a better name and define datastructure for values
const firstPath = first(paths) const firstPath = first(paths)
const parentPath = initial(firstPath) const parentPath = initial(firstPath)
const parent = getIn(json, parentPath) const parent = getIn(json, parentPath)
@ -149,8 +147,7 @@ export function replace (json, paths, values, nextKeys) { // TODO: find a bette
value: entry.value value: entry.value
})) }))
] ]
} } else { // parent is Object
else { // parent is Object
// if we're going to replace an existing object with key "a" with a new // if we're going to replace an existing object with key "a" with a new
// key "a", we must not create a new unique name "a (copy)". // key "a", we must not create a new unique name "a (copy)".
const removeKeys = new Set(paths.map(path => last(path))) const removeKeys = new Set(paths.map(path => last(path)))
@ -191,13 +188,13 @@ export function replace (json, paths, values, nextKeys) { // TODO: find a bette
*/ */
export function duplicate (doc, state, paths) { export function duplicate (doc, state, paths) {
// FIXME: here we assume selection.paths is sorted correctly, that's a dangerous assumption // FIXME: here we assume selection.paths is sorted correctly, that's a dangerous assumption
const lastPath = last(paths) const lastPath = last(paths)
const parentPath = initial(lastPath) const parentPath = initial(lastPath)
const beforeKey = last(lastPath) const beforeKey = last(lastPath)
const props = getIn(state, parentPath.concat(STATE_PROPS)) const props = getIn(state, parentPath.concat(STATE_PROPS))
const nextKeys = getNextKeys(props, beforeKey, false) const nextKeys = getNextKeys(props, beforeKey, false)
const parent = getIn(doc, parentPath) const parent = getIn(doc, parentPath)
if (Array.isArray(parent)) { if (Array.isArray(parent)) {
const lastPath = last(paths) const lastPath = last(paths)
const offset = lastPath ? (parseInt(last(lastPath), 10) + 1) : 0 const offset = lastPath ? (parseInt(last(lastPath), 10) + 1) : 0
@ -243,7 +240,7 @@ export function insert (doc, state, selection, values) {
const nextKeys = getNextKeys(props, beforeKey, true) const nextKeys = getNextKeys(props, beforeKey, true)
const operations = insertBefore(doc, selection.beforePath, values, nextKeys) const operations = insertBefore(doc, selection.beforePath, values, nextKeys)
// TODO: move calculation of nextKeys inside insertBefore? // TODO: move calculation of nextKeys inside insertBefore?
return operations return operations
} else if (selection.appendPath) { } else if (selection.appendPath) {
const operations = append(doc, selection.appendPath, values) const operations = append(doc, selection.appendPath, values)
@ -257,23 +254,23 @@ export function insert (doc, state, selection, values) {
const nextKeys = getNextKeys(props, beforeKey, true) const nextKeys = getNextKeys(props, beforeKey, true)
const operations = replace(doc, selection.paths, values, nextKeys) const operations = replace(doc, selection.paths, values, nextKeys)
// TODO: move calculation of nextKeys inside replace? // TODO: move calculation of nextKeys inside replace?
return operations return operations
} }
} }
export function createNewValue (doc, selection, type) { export function createNewValue (doc, selection, type) {
switch (type) { switch (type) {
case 'value': case 'value':
return '' return ''
case 'object': case 'object':
return {} return {}
case 'array': case 'array':
return [] return []
case 'structure': case 'structure':
const parentPath = getParentPath(selection) const parentPath = getParentPath(selection)
const parent = getIn(doc, parentPath) const parent = getIn(doc, parentPath)
@ -281,8 +278,8 @@ export function createNewValue (doc, selection, type) {
const jsonExample = first(parent) const jsonExample = first(parent)
const structure = cloneDeepWith(jsonExample, (value) => { const structure = cloneDeepWith(jsonExample, (value) => {
return isObjectOrArray(value) return isObjectOrArray(value)
? undefined // leave as is ? undefined // leave as is
: '' : ''
}) })
console.log('structure', jsonExample, structure) console.log('structure', jsonExample, structure)
@ -316,16 +313,16 @@ export function remove (path) {
*/ */
export function removeAll (paths) { export function removeAll (paths) {
return paths return paths
.map(path => ({ .map(path => ({
op: 'remove', op: 'remove',
path: compileJSONPointer(path) path: compileJSONPointer(path)
})) }))
.reverse() // reverse is needed for arrays: delete the last index first .reverse() // reverse is needed for arrays: delete the last index first
} }
// helper function to move a key down in an object, // helper function to move a key down in an object,
// so another key can get positioned before the moved down keys // so another key can get positioned before the moved down keys
function moveDown(parentPath, key) { function moveDown (parentPath, key) {
return { return {
op: 'move', op: 'move',
from: compileJSONPointer(parentPath.concat(key)), from: compileJSONPointer(parentPath.concat(key)),

View File

@ -3,7 +3,6 @@ import { STATE_SEARCH_PROPERTY, STATE_SEARCH_VALUE } from '../constants.js'
import { existsIn, setIn } from '../utils/immutabilityHelpers.js' import { existsIn, setIn } from '../utils/immutabilityHelpers.js'
import { valueType } from '../utils/typeUtils.js' import { valueType } from '../utils/typeUtils.js'
/** /**
* @typedef {{path: Path, what: Symbol}} SearchItem * @typedef {{path: Path, what: Symbol}} SearchItem
*/ */
@ -101,7 +100,7 @@ export function searchPrevious (searchResult) {
} }
function searchRecursive (key, doc, searchText) { function searchRecursive (key, doc, searchText) {
let results = undefined let results
if (typeof key === 'string' && containsCaseInsensitive(key, searchText)) { if (typeof key === 'string' && containsCaseInsensitive(key, searchText)) {
results = createOrAdd(results, STATE_SEARCH_PROPERTY, 'search') results = createOrAdd(results, STATE_SEARCH_PROPERTY, 'search')
@ -110,14 +109,14 @@ function searchRecursive (key, doc, searchText) {
const type = valueType(doc) const type = valueType(doc)
if (type === 'array') { if (type === 'array') {
doc.forEach((item, index) => { doc.forEach((item, index) => {
let childResults = searchRecursive(index, item, searchText) const childResults = searchRecursive(index, item, searchText)
if (childResults) { if (childResults) {
results = createOrAdd(results, index, childResults) results = createOrAdd(results, index, childResults)
} }
}) })
} else if (type === 'object') { } else if (type === 'object') {
Object.keys(doc).forEach(prop => { Object.keys(doc).forEach(prop => {
let childResults = searchRecursive(prop, doc[prop], searchText) const childResults = searchRecursive(prop, doc[prop], searchText)
if (childResults) { if (childResults) {
results = createOrAdd(results, prop, childResults) results = createOrAdd(results, prop, childResults)
} }
@ -167,7 +166,7 @@ function flattenSearch (searchResult) {
return resultArray return resultArray
} }
function createOrAdd(object, key, value) { function createOrAdd (object, key, value) {
if (object) { if (object) {
object[key] = value object[key] = value
return object return object

View File

@ -3,14 +3,14 @@ import { VALIDATION_ERROR } from '../constants.js'
import { existsIn, setIn } from '../utils/immutabilityHelpers.js' import { existsIn, setIn } from '../utils/immutabilityHelpers.js'
/** /**
* Create a nested map with validation errors, * Create a nested map with validation errors,
* and also create error messages for the parent nodes of the nodes having an error. * and also create error messages for the parent nodes of the nodes having an error.
* *
* @param {ValidationError[]} validationErrors * @param {ValidationError[]} validationErrors
* @return {Object.<string, string> | undefined} Returns a nested object containing * @return {Object.<string, string> | undefined} Returns a nested object containing
*/ */
export function mapValidationErrors (validationErrors) { export function mapValidationErrors (validationErrors) {
let object = undefined let object
validationErrors.forEach(validationError => { validationErrors.forEach(validationError => {
const errorPath = validationError.path.concat([VALIDATION_ERROR]) const errorPath = validationError.path.concat([VALIDATION_ERROR])

View File

@ -30,7 +30,7 @@ describe('validation', () => {
expected[VALIDATION_ERROR] = { isChildError: true, path: [], message: message3 } expected[VALIDATION_ERROR] = { isChildError: true, path: [], message: message3 }
expected.pupils[VALIDATION_ERROR] = { isChildError: true, path: ['pupils'], message: message3 } expected.pupils[VALIDATION_ERROR] = { isChildError: true, path: ['pupils'], message: message3 }
expected.pupils[2][VALIDATION_ERROR] = { isChildError: true, path: ['pupils', 2], message: message3 } expected.pupils[2][VALIDATION_ERROR] = { isChildError: true, path: ['pupils', 2], message: message3 }
const actual = mapValidationErrors(validationErrorsList) const actual = mapValidationErrors(validationErrorsList)
assert.deepStrictEqual(actual, expected) assert.deepStrictEqual(actual, expected)
@ -51,9 +51,9 @@ describe('validation', () => {
}, },
[VALIDATION_ERROR]: error2 [VALIDATION_ERROR]: error2
} }
const actual = mapValidationErrors(validationErrorsList) const actual = mapValidationErrors(validationErrorsList)
assert.deepStrictEqual(actual, expected) assert.deepStrictEqual(actual, expected)
}) })
}) })

View File

@ -1,5 +1,5 @@
import { isObject } from './typeUtils.js' import { isObject } from './typeUtils.js'
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js' import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
const MAX_ITEM_PATHS_COLLECTION = 10000 const MAX_ITEM_PATHS_COLLECTION = 10000
const EMPTY_ARRAY = [] const EMPTY_ARRAY = []
@ -51,7 +51,7 @@ export function strictShallowEqual (a, b) {
return true return true
} }
export function compareArrays(a, b) { export function compareArrays (a, b) {
const minLength = Math.min(a.length, b.length) const minLength = Math.min(a.length, b.length)
for (let i = 0; i < minLength; i++) { for (let i = 0; i < minLength; i++) {
@ -75,7 +75,7 @@ export function compareArrays(a, b) {
*/ */
export function getNestedPaths (array, includeObjects = false) { export function getNestedPaths (array, includeObjects = false) {
const pathsMap = {} const pathsMap = {}
if (!Array.isArray(array)) { if (!Array.isArray(array)) {
throw new TypeError('Array expected') throw new TypeError('Array expected')
} }

View File

@ -1,4 +1,4 @@
import assert from "assert" import assert from 'assert'
import { compareArrays, getNestedPaths } from './arrayUtils.js' import { compareArrays, getNestedPaths } from './arrayUtils.js'
describe('arrayUtils', () => { describe('arrayUtils', () => {

View File

@ -5,7 +5,7 @@
* @param {Element} element An HTML DOM element like a DIV * @param {Element} element An HTML DOM element like a DIV
* @return {string} * @return {string}
*/ */
export function getPlainText(element) { export function getPlainText (element) {
return unescapeHTML(traverseInnerText(element)) return unescapeHTML(traverseInnerText(element))
} }
@ -14,7 +14,7 @@ export function getPlainText(element) {
* @param {Element} element An HTML DOM element like a DIV * @param {Element} element An HTML DOM element like a DIV
* @param {string} text * @param {string} text
*/ */
export function setPlainText(element, text) { export function setPlainText (element, text) {
element.innerHTML = escapeHTML(text) element.innerHTML = escapeHTML(text)
} }
/** /**
@ -26,8 +26,7 @@ export function setPlainText(element, text) {
export function escapeHTML (text, escapeUnicode = false) { export function escapeHTML (text, escapeUnicode = false) {
if (typeof text !== 'string') { if (typeof text !== 'string') {
return String(text) return String(text)
} } else {
else {
let htmlEscaped = String(text) let htmlEscaped = String(text)
if (escapeUnicode === true) { if (escapeUnicode === true) {
// FIXME: should not unescape the just created non-breaking spaces \u00A0 ? // FIXME: should not unescape the just created non-breaking spaces \u00A0 ?
@ -36,8 +35,8 @@ export function escapeHTML (text, escapeUnicode = false) {
htmlEscaped = htmlEscaped htmlEscaped = htmlEscaped
.replace(/ {2}/g, ' \u00A0') // replace double space with an nbsp and space .replace(/ {2}/g, ' \u00A0') // replace double space with an nbsp and space
.replace(/^ /, '\u00A0') // space at start .replace(/^ /, '\u00A0') // space at start
.replace(/ $/, '\u00A0') // space at end .replace(/ $/, '\u00A0') // space at end
const json = JSON.stringify(htmlEscaped) const json = JSON.stringify(htmlEscaped)
return json.substring(1, json.length - 1) // remove enclosing double quotes return json.substring(1, json.length - 1) // remove enclosing double quotes
@ -54,8 +53,8 @@ export function escapeUnicodeChars (text) {
// see https://www.wikiwand.com/en/UTF-16 // see https://www.wikiwand.com/en/UTF-16
// note: we leave surrogate pairs as two individual chars, // note: we leave surrogate pairs as two individual chars,
// as JSON doesn't interpret them as a single unicode char. // as JSON doesn't interpret them as a single unicode char.
return text.replace(/[\u007F-\uFFFF]/g, function(c) { return text.replace(/[\u007F-\uFFFF]/g, function (c) {
return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4) return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
}) })
} }
@ -68,7 +67,7 @@ export function unescapeHTML (escapedText) {
const json = '"' + escapeJSON(escapedText) + '"' const json = '"' + escapeJSON(escapedText) + '"'
const htmlEscaped = JSON.parse(json) // TODO: replace with a JSON.parse which does do linting and give an informative error const htmlEscaped = JSON.parse(json) // TODO: replace with a JSON.parse which does do linting and give an informative error
return htmlEscaped.replace(/\u00A0/g, ' ') // nbsp character return htmlEscaped.replace(/\u00A0/g, ' ') // nbsp character
} }
/** /**
@ -88,21 +87,18 @@ export function escapeJSON (text) {
let c = text.charAt(i) let c = text.charAt(i)
if (c === '\n') { if (c === '\n') {
escaped += '\\n' escaped += '\\n'
} } else if (c === '\\') {
else if (c === '\\') {
escaped += c escaped += c
i++ i++
c = text.charAt(i) c = text.charAt(i)
if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) { if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) {
escaped += '\\' // no valid escape character escaped += '\\' // no valid escape character
} }
escaped += c escaped += c
} } else if (c === '"') {
else if (c === '"') {
escaped += '\\"' escaped += '\\"'
} } else {
else {
escaped += c escaped += c
} }
i++ i++
@ -192,7 +188,7 @@ export function isContentEditableDiv (element) {
return (element.nodeName === 'DIV' && element.contentEditable === 'true') return (element.nodeName === 'DIV' && element.contentEditable === 'true')
} }
function hasAttribute(element, name, value) { function hasAttribute (element, name, value) {
return typeof element.getAttribute === 'function' && element.getAttribute(name) === value return typeof element.getAttribute === 'function' && element.getAttribute(name) === value
} }

View File

@ -18,4 +18,4 @@ describe('domUtils', () => {
// TODO: test unescapeHTML more thoroughly // TODO: test unescapeHTML more thoroughly
}) })
}) })

View File

@ -1,4 +1,4 @@
import { isObjectOrArray } from './typeUtils.js' import { isObjectOrArray } from './typeUtils.js'
/** /**
* Immutability helpers * Immutability helpers
@ -23,20 +23,22 @@ export function shallowClone (value) {
const copy = value.slice() const copy = value.slice()
// copy all symbols // copy all symbols
Object.getOwnPropertySymbols(value).forEach(symbol => copy[symbol] = value[symbol]) Object.getOwnPropertySymbols(value).forEach(symbol => {
copy[symbol] = value[symbol]
})
return copy return copy
} } else if (typeof value === 'object') {
else if (typeof value === 'object') {
// copy object properties // copy object properties
const copy = { ...value } const copy = { ...value }
// copy all symbols // copy all symbols
Object.getOwnPropertySymbols(value).forEach(symbol => copy[symbol] = value[symbol]) Object.getOwnPropertySymbols(value).forEach(symbol => {
copy[symbol] = value[symbol]
})
return copy return copy
} } else {
else {
return value return value
} }
} }
@ -53,8 +55,7 @@ export function applyProp (object, key, value) {
if (object[key] === value) { if (object[key] === value) {
// return original object unchanged when the new value is identical to the old one // return original object unchanged when the new value is identical to the old one
return object return object
} } else {
else {
const updatedObject = shallowClone(object) const updatedObject = shallowClone(object)
updatedObject[key] = value updatedObject[key] = value
return updatedObject return updatedObject
@ -73,11 +74,10 @@ export function getIn (object, path) {
let value = object let value = object
let i = 0 let i = 0
while(i < path.length) { while (i < path.length) {
if (isObjectOrArray(value)) { if (isObjectOrArray(value)) {
value = value[path[i]] value = value[path[i]]
} } else {
else {
value = undefined value = undefined
} }
@ -171,14 +171,12 @@ export function deleteIn (object, path) {
if (!(key in object)) { if (!(key in object)) {
// key doesn't exist. return object unchanged // key doesn't exist. return object unchanged
return object return object
} } else {
else {
const updatedObject = shallowClone(object) const updatedObject = shallowClone(object)
if (Array.isArray(updatedObject)) { if (Array.isArray(updatedObject)) {
updatedObject.splice(key, 1) updatedObject.splice(key, 1)
} } else {
else {
delete updatedObject[key] delete updatedObject[key]
} }
@ -230,7 +228,7 @@ export function transform (json, callback, path = []) {
const updated1 = callback(json, path) const updated1 = callback(json, path)
if (Array.isArray(json)) { // array if (Array.isArray(json)) { // array
let updated2 = undefined let updated2
for (let i = 0; i < updated1.length; i++) { for (let i = 0; i < updated1.length; i++) {
const before = updated1[i] const before = updated1[i]
@ -246,13 +244,12 @@ export function transform (json, callback, path = []) {
} }
} }
return updated2 ? updated2 : updated1 return updated2 || updated1
} } else if (json && typeof json === 'object') { // object
else if (json && typeof json === 'object') { // object let updated2
let updated2 = undefined
for (let key in updated1) { for (const key in updated1) {
if (updated1.hasOwnProperty(key)) { if (Object.hasOwnProperty.call(updated1, key)) {
const before = updated1[key] const before = updated1[key]
const after = transform(before, callback, path.concat(key)) const after = transform(before, callback, path.concat(key))
if (after !== before) { if (after !== before) {
@ -264,9 +261,8 @@ export function transform (json, callback, path = []) {
} }
} }
return updated2 ? updated2 : updated1 return updated2 || updated1
} } else { // number, string, boolean, null
else { // number, string, boolean, null
return updated1 return updated1
} }
} }

View File

@ -27,7 +27,7 @@ describe('immutabilityHelpers', () => {
] ]
} }
assert.deepStrictEqual(getIn(obj, ['a', 'b']), {c: 2}) assert.deepStrictEqual(getIn(obj, ['a', 'b']), { c: 2 })
assert.deepStrictEqual(getIn(obj, ['e', '1', 'f']), 5) assert.deepStrictEqual(getIn(obj, ['e', '1', 'f']), 5)
assert.deepStrictEqual(getIn(obj, ['e', '999', 'f']), undefined) assert.deepStrictEqual(getIn(obj, ['e', '999', 'f']), undefined)
assert.deepStrictEqual(getIn(obj, ['non', 'existing', 'path']), undefined) assert.deepStrictEqual(getIn(obj, ['non', 'existing', 'path']), undefined)
@ -92,7 +92,7 @@ describe('immutabilityHelpers', () => {
a: [ a: [
, ,
, ,
{ c : 4 } { c: 4 }
] ]
}) })
assert.deepStrictEqual(obj, {}) assert.deepStrictEqual(obj, {})
@ -104,7 +104,7 @@ describe('immutabilityHelpers', () => {
d: 3 d: 3
} }
assert.throws(() => setIn(obj, ['a', 'b', 'c'], 4), /Path does not exist/) assert.throws(() => setIn(obj, ['a', 'b', 'c'], 4), /Path does not exist/)
}) })
it('setIn replace value inside nested array', () => { it('setIn replace value inside nested array', () => {
@ -136,7 +136,7 @@ describe('immutabilityHelpers', () => {
}) })
it('setIn identical value should return the original object', () => { it('setIn identical value should return the original object', () => {
const obj = {a:1, b:2} const obj = { a: 1, b: 2 }
const updated = setIn(obj, ['b'], 2) const updated = setIn(obj, ['b'], 2)
@ -144,7 +144,7 @@ describe('immutabilityHelpers', () => {
}) })
it('setIn identical value should return the original object (2)', () => { it('setIn identical value should return the original object (2)', () => {
const obj = {a:1, b: { c: 2}} const obj = { a: 1, b: { c: 2 } }
const updated = setIn(obj, ['b', 'c'], 2) const updated = setIn(obj, ['b', 'c'], 2)
@ -194,10 +194,10 @@ describe('immutabilityHelpers', () => {
d: 3 d: 3
} }
const updated = updateIn(obj, ['a', 'b' ], (obj) => [1,2,3]) const updated = updateIn(obj, ['a', 'b'], (obj) => [1, 2, 3])
assert.deepStrictEqual(updated, { assert.deepStrictEqual(updated, {
a: { a: {
b: [1,2,3] b: [1, 2, 3]
}, },
d: 3 d: 3
}) })
@ -213,7 +213,7 @@ describe('immutabilityHelpers', () => {
d: 3 d: 3
} }
const updated = updateIn(obj, ['a', 'e' ], (value) => 'foo-' + value) const updated = updateIn(obj, ['a', 'e'], (value) => 'foo-' + value)
assert.deepStrictEqual(updated, { assert.deepStrictEqual(updated, {
a: { a: {
b: { b: {
@ -231,7 +231,7 @@ describe('immutabilityHelpers', () => {
b: 3 b: 3
} }
const updated = updateIn(obj, ['b' ], (value) => 3) const updated = updateIn(obj, ['b'], (value) => 3)
assert.strictEqual(updated, obj) assert.strictEqual(updated, obj)
}) })
@ -273,7 +273,7 @@ describe('immutabilityHelpers', () => {
it('deleteIn array', () => { it('deleteIn array', () => {
const obj = { const obj = {
a: { a: {
b: [1, {c: 2, d: 3} , 4] b: [1, { c: 2, d: 3 }, 4]
}, },
e: 5 e: 5
} }
@ -281,7 +281,7 @@ describe('immutabilityHelpers', () => {
const updated = deleteIn(obj, ['a', 'b', '1', 'c']) const updated = deleteIn(obj, ['a', 'b', '1', 'c'])
assert.deepStrictEqual(updated, { assert.deepStrictEqual(updated, {
a: { a: {
b: [1, {d: 3} , 4] b: [1, { d: 3 }, 4]
}, },
e: 5 e: 5
}) })
@ -289,7 +289,7 @@ describe('immutabilityHelpers', () => {
// original should be unchanged // original should be unchanged
assert.deepStrictEqual(obj, { assert.deepStrictEqual(obj, {
a: { a: {
b: [1, {c: 2, d: 3} , 4] b: [1, { c: 2, d: 3 }, 4]
}, },
e: 5 e: 5
}) })
@ -298,54 +298,54 @@ describe('immutabilityHelpers', () => {
}) })
it('deleteIn non existing path', () => { it('deleteIn non existing path', () => {
const obj = { a: {}} const obj = { a: {} }
const updated = deleteIn(obj, ['a', 'b']) const updated = deleteIn(obj, ['a', 'b'])
assert.strictEqual(updated, obj) assert.strictEqual(updated, obj)
}) })
it('insertAt', () => { it('insertAt', () => {
const obj = { a: [1,2,3]} const obj = { a: [1, 2, 3] }
const updated = insertAt(obj, ['a', '2'], 8) const updated = insertAt(obj, ['a', '2'], 8)
assert.deepStrictEqual(updated, {a: [1,2,8,3]}) assert.deepStrictEqual(updated, { a: [1, 2, 8, 3] })
}) })
it('insertAt should copy symbols', () => { it('insertAt should copy symbols', () => {
const symbol = Symbol('test symbol') const symbol = Symbol('test symbol')
const obj = { a: [1,2,3] } const obj = { a: [1, 2, 3] }
obj.a[symbol] = 'test' obj.a[symbol] = 'test'
const updated = insertAt(obj, ['a', '2'], 8) const updated = insertAt(obj, ['a', '2'], 8)
const expected = {a: [1,2,8,3]} const expected = { a: [1, 2, 8, 3] }
expected.a[symbol] = 'test' expected.a[symbol] = 'test'
assert.deepStrictEqual(updated, expected) assert.deepStrictEqual(updated, expected)
}) })
it('transform (no change)', () => { it('transform (no change)', () => {
const json = {a: [1,2,3], b: {c: 4}} const json = { a: [1, 2, 3], b: { c: 4 } }
const updated = transform(json, (value, path) => value) const updated = transform(json, (value, path) => value)
assert.strictEqual(updated, json) assert.strictEqual(updated, json)
}) })
it('transform (change based on value)', () => { it('transform (change based on value)', () => {
const json = {a: [1,2,3], b: {c: 4}} const json = { a: [1, 2, 3], b: { c: 4 } }
const updated = transform(json, const updated = transform(json,
(value, path) => value === 2 ? 20 : value) (value, path) => value === 2 ? 20 : value)
const expected = {a: [1,20,3], b: {c: 4}} const expected = { a: [1, 20, 3], b: { c: 4 } }
assert.deepStrictEqual(updated, expected) assert.deepStrictEqual(updated, expected)
assert.strictEqual(updated.b, json.b) // should not have replaced b assert.strictEqual(updated.b, json.b) // should not have replaced b
}) })
it('transform (change based on path)', () => { it('transform (change based on path)', () => {
const json = {a: [1,2,3], b: {c: 4}} const json = { a: [1, 2, 3], b: { c: 4 } }
const updated = transform(json, const updated = transform(json,
(value, path) => path.join('.') === 'a.1' ? 20 : value) (value, path) => path.join('.') === 'a.1' ? 20 : value)
const expected = {a: [1,20,3], b: {c: 4}} const expected = { a: [1, 20, 3], b: { c: 4 } }
assert.deepStrictEqual(updated, expected) assert.deepStrictEqual(updated, expected)
assert.strictEqual(updated.b, json.b) // should not have replaced b assert.strictEqual(updated.b, json.b) // should not have replaced b
@ -353,12 +353,12 @@ describe('immutabilityHelpers', () => {
it('existsIn', () => { it('existsIn', () => {
const json = { const json = {
"obj": { obj: {
"arr": [1,2, {"first":3,"last":4}] arr: [1, 2, { first: 3, last: 4 }]
}, },
"str": "hello world", str: 'hello world',
"nill": null, nill: null,
"bool": false bool: false
} }
assert.deepStrictEqual(existsIn(json, ['obj', 'arr', 2, 'first']), true) assert.deepStrictEqual(existsIn(json, ['obj', 'arr', 2, 'first']), true)
@ -366,17 +366,8 @@ describe('immutabilityHelpers', () => {
assert.deepStrictEqual(existsIn(json, ['obj', 'foo', 'bar']), false) assert.deepStrictEqual(existsIn(json, ['obj', 'foo', 'bar']), false)
assert.deepStrictEqual(existsIn(json, []), true) assert.deepStrictEqual(existsIn(json, []), true)
}) })
it('existsIn should find symbols', () => {
const json = {
"obj": {
"arr": [1,2, {"first":3,"last":4}]
},
"str": "hello world",
"nill": null,
"bool": false
}
it('existsIn should find symbols', () => {
const symbol = Symbol('mySymbol') const symbol = Symbol('mySymbol')
const arrWithSymbol = [1, 2, 3] const arrWithSymbol = [1, 2, 3]
@ -387,4 +378,4 @@ describe('immutabilityHelpers', () => {
assert.deepStrictEqual(existsIn({ a: 1, [symbol]: 2 }, [symbol]), true) assert.deepStrictEqual(existsIn({ a: 1, [symbol]: 2 }, [symbol]), true)
assert.deepStrictEqual(existsIn({ a: 1 }, [symbol]), false) assert.deepStrictEqual(existsIn({ a: 1 }, [symbol]), false)
}) })
}) })

View File

@ -22,8 +22,8 @@ export function immutableJSONPatch (json, operations) {
for (let i = 0; i < operations.length; i++) { for (let i = 0; i < operations.length; i++) {
const operation = operations[i] const operation = operations[i]
const path = operation.path != undefined ? parseJSONPointer(operation.path) : null const path = operation.path !== undefined ? parseJSONPointer(operation.path) : null
const from = operation.from != undefined ? parseJSONPointer(operation.from) : null const from = operation.from !== undefined ? parseJSONPointer(operation.from) : null
switch (operation.op) { switch (operation.op) {
case 'add': { case 'add': {
@ -85,7 +85,7 @@ export function immutableJSONPatch (json, operations) {
// when a test fails, cancel the whole patch and return the error // when a test fails, cancel the whole patch and return the error
const error = test(updatedJson, path, operation.value) const error = test(updatedJson, path, operation.value)
if (error) { if (error) {
return { json, revert: [], error} return { json, revert: [], error }
} }
break break
@ -160,12 +160,12 @@ export function add (json, path, value) {
const parent = getIn(json, initial(path)) const parent = getIn(json, initial(path))
const parentIsArray = Array.isArray(parent) const parentIsArray = Array.isArray(parent)
const oldValue = parentIsArray const oldValue = parentIsArray
? undefined ? undefined
: getIn(json, resolvedPath) : getIn(json, resolvedPath)
const updatedJson = parentIsArray const updatedJson = parentIsArray
? insertAt(json, resolvedPath, value) ? insertAt(json, resolvedPath, value)
: setIn(json, resolvedPath, value) : setIn(json, resolvedPath, value)
if (!parentIsArray && existsIn(json, resolvedPath)) { if (!parentIsArray && existsIn(json, resolvedPath)) {
return { return {
@ -176,8 +176,7 @@ export function add (json, path, value) {
value: oldValue value: oldValue
}] }]
} }
} } else {
else {
return { return {
json: updatedJson, json: updatedJson,
revert: [{ revert: [{
@ -219,8 +218,8 @@ export function move (json, path, from) {
const removedJson = remove(json, from).json const removedJson = remove(json, from).json
const updatedJson = parentIsArray const updatedJson = parentIsArray
? insertAt(removedJson, resolvedPath, value) ? insertAt(removedJson, resolvedPath, value)
: setIn(removedJson, resolvedPath, value) : setIn(removedJson, resolvedPath, value)
if (oldValue !== undefined && !parentIsArray) { if (oldValue !== undefined && !parentIsArray) {
// replaces an existing value in an object // replaces an existing value in an object
@ -239,8 +238,7 @@ export function move (json, path, from) {
} }
] ]
} }
} } else {
else {
return { return {
json: updatedJson, json: updatedJson,
revert: [ revert: [
@ -287,6 +285,6 @@ export function resolvePathIndex (json, path) {
const parent = getIn(json, initial(path)) const parent = getIn(json, initial(path))
return (path[path.length - 1] === '-') return (path[path.length - 1] === '-')
? path.slice(0, path.length - 1).concat(parent.length) ? path.slice(0, path.length - 1).concat(parent.length)
: path : path
} }

View File

@ -14,92 +14,92 @@ describe('immutableJSONPatch', () => {
it('jsonpatch add', () => { it('jsonpatch add', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 2} obj: { a: 2 }
} }
const patch = [ const patch = [
{op: 'add', path: '/obj/b', value: {foo: 'bar'}} { op: 'add', path: '/obj/b', value: { foo: 'bar' } }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 2, b: {foo: 'bar'}} obj: { a: 2, b: { foo: 'bar' } }
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'remove', path: '/obj/b'} { op: 'remove', path: '/obj/b' }
]) ])
assert.strictEqual(result.json.arr, json.arr) assert.strictEqual(result.json.arr, json.arr)
}) })
it('jsonpatch add: insert in matrix', () => { it('jsonpatch add: insert in matrix', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 2} obj: { a: 2 }
} }
const patch = [ const patch = [
{op: 'add', path: '/arr/1', value: 4} { op: 'add', path: '/arr/1', value: 4 }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,4,2,3], arr: [1, 4, 2, 3],
obj: {a : 2} obj: { a: 2 }
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'remove', path: '/arr/1'} { op: 'remove', path: '/arr/1' }
]) ])
assert.strictEqual(result.json.obj, json.obj) assert.strictEqual(result.json.obj, json.obj)
}) })
it('jsonpatch add: append to matrix', () => { it('jsonpatch add: append to matrix', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 2} obj: { a: 2 }
} }
const patch = [ const patch = [
{op: 'add', path: '/arr/-', value: 4} { op: 'add', path: '/arr/-', value: 4 }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,2,3,4], arr: [1, 2, 3, 4],
obj: {a : 2} obj: { a: 2 }
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'remove', path: '/arr/3'} { op: 'remove', path: '/arr/3' }
]) ])
assert.strictEqual(result.json.obj, json.obj) assert.strictEqual(result.json.obj, json.obj)
}) })
it('jsonpatch remove', () => { it('jsonpatch remove', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4}, obj: { a: 4 },
unchanged: {} unchanged: {}
} }
const patch = [ const patch = [
{op: 'remove', path: '/obj/a'}, { op: 'remove', path: '/obj/a' },
{op: 'remove', path: '/arr/1'}, { op: 'remove', path: '/arr/1' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,3], arr: [1, 3],
obj: {}, obj: {},
unchanged: {} unchanged: {}
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'add', path: '/arr/1', value: 2}, { op: 'add', path: '/arr/1', value: 2 },
{op: 'add', path: '/obj/a', value: 4} { op: 'add', path: '/obj/a', value: 4 }
]) ])
// test revert // test revert
@ -112,26 +112,26 @@ describe('immutableJSONPatch', () => {
it('jsonpatch replace', () => { it('jsonpatch replace', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4}, obj: { a: 4 },
unchanged: {} unchanged: {}
} }
const patch = [ const patch = [
{op: 'replace', path: '/obj/a', value: 400}, { op: 'replace', path: '/obj/a', value: 400 },
{op: 'replace', path: '/arr/1', value: 200}, { op: 'replace', path: '/arr/1', value: 200 }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,200,3], arr: [1, 200, 3],
obj: {a: 400}, obj: { a: 400 },
unchanged: {} unchanged: {}
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'replace', path: '/arr/1', value: 2}, { op: 'replace', path: '/arr/1', value: 2 },
{op: 'replace', path: '/obj/a', value: 4} { op: 'replace', path: '/obj/a', value: 4 }
]) ])
// test revert // test revert
@ -139,8 +139,8 @@ describe('immutableJSONPatch', () => {
assert.deepStrictEqual(result2.json, json) assert.deepStrictEqual(result2.json, json)
assert.deepStrictEqual(result2.revert, [ assert.deepStrictEqual(result2.revert, [
{op: 'replace', path: '/obj/a', value: 400}, { op: 'replace', path: '/obj/a', value: 400 },
{op: 'replace', path: '/arr/1', value: 200} { op: 'replace', path: '/arr/1', value: 200 }
]) ])
assert.strictEqual(result.json.unchanged, json.unchanged) assert.strictEqual(result.json.unchanged, json.unchanged)
}) })
@ -150,31 +150,31 @@ describe('immutableJSONPatch', () => {
a: 2 a: 2
} }
const patch = [ const patch = [
{op: 'replace', path: '', value: {b: 3}} { op: 'replace', path: '', value: { b: 3 } }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, {b: 3}) assert.deepStrictEqual(result.json, { b: 3 })
}) })
it('jsonpatch copy', () => { it('jsonpatch copy', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4} obj: { a: 4 }
} }
const patch = [ const patch = [
{op: 'copy', from: '/obj', path: '/arr/2'}, { op: 'copy', from: '/obj', path: '/arr/2' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1, 2, {a:4}, 3], arr: [1, 2, { a: 4 }, 3],
obj: {a: 4} obj: { a: 4 }
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'remove', path: '/arr/2'} { op: 'remove', path: '/arr/2' }
]) ])
// test revert // test revert
@ -182,7 +182,7 @@ describe('immutableJSONPatch', () => {
assert.deepStrictEqual(result2.json, json) assert.deepStrictEqual(result2.json, json)
assert.deepStrictEqual(result2.revert, [ assert.deepStrictEqual(result2.revert, [
{op: 'add', path: '/arr/2', value: {a: 4}} { op: 'add', path: '/arr/2', value: { a: 4 } }
]) ])
assert.strictEqual(result.json.obj, json.obj) assert.strictEqual(result.json.obj, json.obj)
assert.strictEqual(result.json.arr[2], json.obj) assert.strictEqual(result.json.arr[2], json.obj)
@ -190,24 +190,24 @@ describe('immutableJSONPatch', () => {
it('jsonpatch move', () => { it('jsonpatch move', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4}, obj: { a: 4 },
unchanged: {} unchanged: {}
} }
const patch = [ const patch = [
{op: 'move', from: '/obj', path: '/arr/2'}, { op: 'move', from: '/obj', path: '/arr/2' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.error, null) assert.deepStrictEqual(result.error, null)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1, 2, {a:4}, 3], arr: [1, 2, { a: 4 }, 3],
unchanged: {} unchanged: {}
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'move', from: '/arr/2', path: '/obj'} { op: 'move', from: '/arr/2', path: '/obj' }
]) ])
// test revert // test revert
@ -223,15 +223,15 @@ describe('immutableJSONPatch', () => {
const json = { a: 2, b: 3 } const json = { a: 2, b: 3 }
const patch = [ const patch = [
{op: 'move', from: '/a', path: '/b'}, { op: 'move', from: '/a', path: '/b' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { b : 2 }) assert.deepStrictEqual(result.json, { b: 2 })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op:'move', from: '/b', path: '/a'}, { op: 'move', from: '/b', path: '/a' },
{op:'add', path:'/b', value: 3} { op: 'add', path: '/b', value: 3 }
]) ])
// test revert // test revert
@ -239,31 +239,31 @@ describe('immutableJSONPatch', () => {
assert.deepStrictEqual(result2.json, json) assert.deepStrictEqual(result2.json, json)
assert.deepStrictEqual(result2.revert, [ assert.deepStrictEqual(result2.revert, [
{op: 'remove', path: '/b'}, { op: 'remove', path: '/b' },
{op: 'move', from: '/a', path: '/b'} { op: 'move', from: '/a', path: '/b' }
]) ])
}) })
it('jsonpatch move and replace (nested)', () => { it('jsonpatch move and replace (nested)', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4}, obj: { a: 4 },
unchanged: {} unchanged: {}
} }
const patch = [ const patch = [
{op: 'move', from: '/obj', path: '/arr'}, { op: 'move', from: '/obj', path: '/arr' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: {a:4}, arr: { a: 4 },
unchanged: {} unchanged: {}
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op:'move', from: '/arr', path: '/obj'}, { op: 'move', from: '/arr', path: '/obj' },
{op:'add', path:'/arr', value: [1,2,3]} { op: 'add', path: '/arr', value: [1, 2, 3] }
]) ])
// test revert // test revert
@ -271,8 +271,8 @@ describe('immutableJSONPatch', () => {
assert.deepStrictEqual(result2.json, json) assert.deepStrictEqual(result2.json, json)
assert.deepStrictEqual(result2.revert, [ assert.deepStrictEqual(result2.revert, [
{op: 'remove', path: '/arr'}, { op: 'remove', path: '/arr' },
{op: 'move', from: '/obj', path: '/arr'} { op: 'move', from: '/obj', path: '/arr' }
]) ])
assert.strictEqual(result.json.unchanged, json.unchanged) assert.strictEqual(result.json.unchanged, json.unchanged)
assert.strictEqual(result2.json.unchanged, json.unchanged) assert.strictEqual(result2.json.unchanged, json.unchanged)
@ -280,44 +280,44 @@ describe('immutableJSONPatch', () => {
it('jsonpatch test (ok)', () => { it('jsonpatch test (ok)', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4} obj: { a: 4 }
} }
const patch = [ const patch = [
{op: 'test', path: '/arr', value: [1,2,3]}, { op: 'test', path: '/arr', value: [1, 2, 3] },
{op: 'add', path: '/added', value: 'ok'} { op: 'add', path: '/added', value: 'ok' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4}, obj: { a: 4 },
added: 'ok' added: 'ok'
}) })
assert.deepStrictEqual(result.revert, [ assert.deepStrictEqual(result.revert, [
{op: 'remove', path: '/added'} { op: 'remove', path: '/added' }
]) ])
}) })
it('jsonpatch test (fail: path not found)', () => { it('jsonpatch test (fail: path not found)', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4} obj: { a: 4 }
} }
const patch = [ const patch = [
{op: 'test', path: '/arr/5', value: [1,2,3]}, { op: 'test', path: '/arr/5', value: [1, 2, 3] },
{op: 'add', path: '/added', value: 'ok'} { op: 'add', path: '/added', value: 'ok' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
// patch shouldn't be applied // patch shouldn't be applied
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4} obj: { a: 4 }
}) })
assert.deepStrictEqual(result.revert, []) assert.deepStrictEqual(result.revert, [])
assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, path not found') assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, path not found')
@ -325,23 +325,23 @@ describe('immutableJSONPatch', () => {
it('jsonpatch test (fail: value not equal)', () => { it('jsonpatch test (fail: value not equal)', () => {
const json = { const json = {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4} obj: { a: 4 }
} }
const patch = [ const patch = [
{op: 'test', path: '/obj', value: {a:4, b: 6}}, { op: 'test', path: '/obj', value: { a: 4, b: 6 } },
{op: 'add', path: '/added', value: 'ok'} { op: 'add', path: '/added', value: 'ok' }
] ]
const result = immutableJSONPatch(json, patch) const result = immutableJSONPatch(json, patch)
// patch shouldn't be applied // patch shouldn't be applied
assert.deepStrictEqual(result.json, { assert.deepStrictEqual(result.json, {
arr: [1,2,3], arr: [1, 2, 3],
obj: {a : 4} obj: { a: 4 }
}) })
assert.deepStrictEqual(result.revert, []) assert.deepStrictEqual(result.revert, [])
assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, value differs') assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, value differs')
}) })
}) })

View File

@ -19,6 +19,6 @@ export function parseJSONPointer (pointer) {
*/ */
export function compileJSONPointer (path) { export function compileJSONPointer (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('')
} }

View File

@ -1,5 +1,5 @@
import assert from 'assert' import assert from 'assert'
import {compileJSONPointer, parseJSONPointer} from './jsonPointer.js' import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
describe('jsonPointer', () => { describe('jsonPointer', () => {
it('parseJSONPointer', () => { it('parseJSONPointer', () => {

View File

@ -12,7 +12,7 @@
* @param {string} code * @param {string} code
* @return {string} * @return {string}
*/ */
export function nameFromKeyCode(code) { export function nameFromKeyCode (code) {
return codes[code] || '' return codes[code] || ''
} }
@ -23,7 +23,7 @@ export function nameFromKeyCode(code) {
* @return {string} * @return {string}
*/ */
export function keyComboFromEvent (event) { export function keyComboFromEvent (event) {
let combi = [] const combi = []
if (event.ctrlKey) { combi.push('Ctrl') } if (event.ctrlKey) { combi.push('Ctrl') }
if (event.metaKey) { combi.push('Command') } if (event.metaKey) { combi.push('Command') }
@ -31,7 +31,7 @@ export function keyComboFromEvent (event) {
if (event.shiftKey) { combi.push('Shift') } if (event.shiftKey) { combi.push('Shift') }
const keyName = nameFromKeyCode(event.which) const keyName = nameFromKeyCode(event.which)
if (!metaCodes[keyName]) { // prevent output like 'Ctrl+Ctrl' if (!metaCodes[keyName]) { // prevent output like 'Ctrl+Ctrl'
combi.push(keyName) combi.push(keyName)
} }
@ -47,8 +47,10 @@ export function keyComboFromEvent (event) {
export function createFindKeyBinding (keyBindings) { export function createFindKeyBinding (keyBindings) {
// turn the map with key bindings by name (multiple per binding) into a map by key combo // turn the map with key bindings by name (multiple per binding) into a map by key combo
const keyCombos = {} const keyCombos = {}
Object.keys(keyBindings).forEach ((name) => { Object.keys(keyBindings).forEach((name) => {
keyBindings[name].forEach(combo => keyCombos[normalizeKeyCombo(combo)] = name) keyBindings[name].forEach(combo => {
keyCombos[normalizeKeyCombo(combo)] = name
})
}) })
return function findKeyBinding (event) { return function findKeyBinding (event) {
@ -81,112 +83,112 @@ function normalizeKeyCombo (combo) {
const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0 const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0
const metaCodes = { const metaCodes = {
'Ctrl': true, Ctrl: true,
'Command': true, Command: true,
'Alt': true, Alt: true,
'Option': true, Option: true,
'Shift': true Shift: true
} }
const codes = { const codes = {
'8': 'Backspace', 8: 'Backspace',
'9': 'Tab', 9: 'Tab',
'13': 'Enter', 13: 'Enter',
'16': 'Shift', 16: 'Shift',
'17': 'Ctrl', 17: 'Ctrl',
'18': 'Alt', 18: 'Alt',
'19': 'Pause_Break', 19: 'Pause_Break',
'20': 'Caps_Lock', 20: 'Caps_Lock',
'27': 'Escape', 27: 'Escape',
'33': 'Page_Up', 33: 'Page_Up',
'34': 'Page_Down', 34: 'Page_Down',
'35': 'End', 35: 'End',
'36': 'Home', 36: 'Home',
'37': 'Left', 37: 'Left',
'38': 'Up', 38: 'Up',
'39': 'Right', 39: 'Right',
'40': 'Down', 40: 'Down',
'45': 'Insert', 45: 'Insert',
'46': 'Delete', 46: 'Delete',
'48': '0', 48: '0',
'49': '1', 49: '1',
'50': '2', 50: '2',
'51': '3', 51: '3',
'52': '4', 52: '4',
'53': '5', 53: '5',
'54': '6', 54: '6',
'55': '7', 55: '7',
'56': '8', 56: '8',
'57': '9', 57: '9',
'65': 'A', 65: 'A',
'66': 'B', 66: 'B',
'67': 'C', 67: 'C',
'68': 'D', 68: 'D',
'69': 'E', 69: 'E',
'70': 'F', 70: 'F',
'71': 'G', 71: 'G',
'72': 'H', 72: 'H',
'73': 'I', 73: 'I',
'74': 'J', 74: 'J',
'75': 'K', 75: 'K',
'76': 'L', 76: 'L',
'77': 'M', 77: 'M',
'78': 'N', 78: 'N',
'79': 'O', 79: 'O',
'80': 'P', 80: 'P',
'81': 'Q', 81: 'Q',
'82': 'R', 82: 'R',
'83': 'S', 83: 'S',
'84': 'T', 84: 'T',
'85': 'U', 85: 'U',
'86': 'V', 86: 'V',
'87': 'W', 87: 'W',
'88': 'X', 88: 'X',
'89': 'Y', 89: 'Y',
'90': 'Z', 90: 'Z',
'91': 'Left_Window_Key', 91: 'Left_Window_Key',
'92': 'Right_Window_Key', 92: 'Right_Window_Key',
'93': 'Select_Key', 93: 'Select_Key',
'96': 'Numpad_0', 96: 'Numpad_0',
'97': 'Numpad_1', 97: 'Numpad_1',
'98': 'Numpad_2', 98: 'Numpad_2',
'99': 'Numpad_3', 99: 'Numpad_3',
'100': 'Numpad_4', 100: 'Numpad_4',
'101': 'Numpad_5', 101: 'Numpad_5',
'102': 'Numpad_6', 102: 'Numpad_6',
'103': 'Numpad_7', 103: 'Numpad_7',
'104': 'Numpad_8', 104: 'Numpad_8',
'105': 'Numpad_9', 105: 'Numpad_9',
'106': 'Numpad_*', 106: 'Numpad_*',
'107': 'Numpad_+', 107: 'Numpad_+',
'109': 'Numpad_-', 109: 'Numpad_-',
'110': 'Numpad_.', 110: 'Numpad_.',
'111': 'Numpad_/', 111: 'Numpad_/',
'112': 'F1', 112: 'F1',
'113': 'F2', 113: 'F2',
'114': 'F3', 114: 'F3',
'115': 'F4', 115: 'F4',
'116': 'F5', 116: 'F5',
'117': 'F6', 117: 'F6',
'118': 'F7', 118: 'F7',
'119': 'F8', 119: 'F8',
'120': 'F9', 120: 'F9',
'121': 'F10', 121: 'F10',
'122': 'F11', 122: 'F11',
'123': 'F12', 123: 'F12',
'144': 'Num_Lock', 144: 'Num_Lock',
'145': 'Scroll_Lock', 145: 'Scroll_Lock',
'186': ';', 186: ';',
'187': '=', 187: '=',
'188': ',', 188: ',',
'189': '-', 189: '-',
'190': '.', 190: '.',
'191': '/', 191: '/',
'192': '`', 192: '`',
'219': '[', 219: '[',
'220': '\\', 220: '\\',
'221': ']', 221: ']',
'222': '\'' 222: '\''
} }
// all secondary characters of the keyboard buttons (used via Shift) // all secondary characters of the keyboard buttons (used via Shift)
@ -195,14 +197,14 @@ const aliases = {
'!': '1', '!': '1',
'@': '2', '@': '2',
'#': '3', '#': '3',
'$': '4', $: '4',
'%': '5', '%': '5',
'^': '6', '^': '6',
'&': '7', '&': '7',
'*': '8', '*': '8',
'(': '9', '(': '9',
')': '0', ')': '0',
'_': '-', _: '-',
'+': '=', '+': '=',
'{': '[', '{': '[',
'}': ']', '}': ']',

View File

@ -1,19 +1,19 @@
/** /**
* Stringify a path like * Stringify a path like
* *
* ["data", 2, "nested", "property"] * ["data", 2, "nested", "property"]
* *
* into a string: * into a string:
* *
* ".data[2].nested.property" * ".data[2].nested.property"
* *
* @param {Path} * @param {Path}
* @return {string} * @return {string}
*/ */
export function stringifyPath (path) { export function stringifyPath (path) {
return path.map(prop => { return path.map(prop => {
return typeof prop === 'number' return typeof prop === 'number'
? `[${prop}]` ? `[${prop}]`
: `.${prop}` : `.${prop}`
}).join('') }).join('')

View File

@ -3,8 +3,8 @@ import { stringifyPath } from './pathUtils.js'
describe('pathUtils', () => { describe('pathUtils', () => {
it('stringifyPath', () => { it('stringifyPath', () => {
assert.strictEqual(stringifyPath(["data", 2, "nested", "property"]), '.data[2].nested.property') assert.strictEqual(stringifyPath(['data', 2, 'nested', 'property']), '.data[2].nested.property')
assert.strictEqual(stringifyPath(['']), '.') assert.strictEqual(stringifyPath(['']), '.')
assert.strictEqual(stringifyPath([]), '') assert.strictEqual(stringifyPath([]), '')
}) })
}) })

View File

@ -9,13 +9,13 @@ import {
describe('stringUtils', () => { describe('stringUtils', () => {
it('findUniqueName', () => { it('findUniqueName', () => {
assert.deepStrictEqual(findUniqueName('other', {'a': true, 'b': true, 'c': true}), 'other') assert.deepStrictEqual(findUniqueName('other', { a: true, b: true, c: true }), 'other')
assert.deepStrictEqual(findUniqueName('b', {'a': true, 'b': true, 'c': true}), 'b (copy)') assert.deepStrictEqual(findUniqueName('b', { a: true, b: true, c: true }), 'b (copy)')
assert.deepStrictEqual(findUniqueName('b', {'a': true, 'b': true, 'c': true, 'b (copy)': true}), 'b (copy 2)') assert.deepStrictEqual(findUniqueName('b', { a: true, b: true, c: true, 'b (copy)': true }), 'b (copy 2)')
assert.deepStrictEqual(findUniqueName('b (copy)', {'a': true, 'b': true, 'c': true, 'b (copy)': true}), 'b (copy 2)') assert.deepStrictEqual(findUniqueName('b (copy)', { a: true, b: true, c: true, 'b (copy)': true }), 'b (copy 2)')
assert.deepStrictEqual(findUniqueName('b', {'a': true, 'b': true, 'c': true, 'b (copy)': true, 'b (copy 2)': true}), 'b (copy 3)') assert.deepStrictEqual(findUniqueName('b', { a: true, b: true, c: true, 'b (copy)': true, 'b (copy 2)': true }), 'b (copy 3)')
assert.deepStrictEqual(findUniqueName('b (copy)', {'a': true, 'b': true, 'c': true, 'b (copy)': true, 'b (copy 2)': true}), 'b (copy 3)') assert.deepStrictEqual(findUniqueName('b (copy)', { a: true, b: true, c: true, 'b (copy)': true, 'b (copy 2)': true }), 'b (copy 3)')
assert.deepStrictEqual(findUniqueName('b (copy 2)', {'a': true, 'b': true, 'c': true, 'b (copy)': true, 'b (copy 2)': true}), 'b (copy 3)') assert.deepStrictEqual(findUniqueName('b (copy 2)', { a: true, b: true, c: true, 'b (copy)': true, 'b (copy 2)': true }), 'b (copy 3)')
}) })
it('toCapital', () => { it('toCapital', () => {

View File

@ -41,7 +41,7 @@ export function isObjectOrArray (value) {
* @param {*} value * @param {*} value
* @return {String} type * @return {String} type
*/ */
export function valueType(value) { export function valueType (value) {
if (value === null) { if (value === null) {
return 'null' return 'null'
} }
@ -85,25 +85,20 @@ export function isUrl (text) {
* @private * @private
*/ */
export function stringConvert (str) { export function stringConvert (str) {
const num = Number(str) // will nicely fail with '123ab' const num = Number(str) // will nicely fail with '123ab'
const numFloat = parseFloat(str) // will nicely fail with ' ' const numFloat = parseFloat(str) // will nicely fail with ' '
if (str === '') { if (str === '') {
return '' return ''
} } else if (str === 'null') {
else if (str === 'null') {
return null return null
} } else if (str === 'true') {
else if (str === 'true') {
return true return true
} } else if (str === 'false') {
else if (str === 'false') {
return false return false
} } else if (!isNaN(num) && !isNaN(numFloat)) {
else if (!isNaN(num) && !isNaN(numFloat)) {
return num return num
} } else {
else {
return str return str
} }
} }

View File

@ -1 +1 @@
// TODO: write unit tests // TODO: write unit tests