From af4fca83fcdb8bc6c07edddb63da76eca47e6b66 Mon Sep 17 00:00:00 2001 From: Edward Chernenko Date: Mon, 3 Feb 2025 19:25:43 +0300 Subject: [PATCH] Attempt to use existing JSON Patch library to apply patches --- lib/entity/LoadedAsset.js | 93 +++++---------------------------------- package.json | 2 +- 2 files changed, 13 insertions(+), 82 deletions(-) diff --git a/lib/entity/LoadedAsset.js b/lib/entity/LoadedAsset.js index 9af453f..61b5afa 100644 --- a/lib/entity/LoadedAsset.js +++ b/lib/entity/LoadedAsset.js @@ -1,7 +1,7 @@ 'use strict'; const { config, util } = require( '..' ), - lodash = require( 'lodash' ); + jsonPatch = require( 'fast-json-patch' ); /** * Represents one asset (parsed JSON file from either vanilla or the mod). Used in AssetDatabase. @@ -106,95 +106,26 @@ class LoadedAsset { * @param {Object} data */ applyPatchInstructions( patch, data ) { - // This is set to true if we find "test" operation that tells us to ignore the rest of this patch. - var patchSkipped = false; - - patch.forEach( ( instruction ) => { - if ( patchSkipped ) { - // This instruction must be ignored, because preceding "test" instruction says so. - return; - } - + for ( let instruction of patch ) { if ( Array.isArray( instruction ) ) { // This is an array of instructions, not a single instruction. // Handle this recursively. this.applyPatchInstructions( instruction, data ); - return; + continue; } - var op = instruction.op, - value = instruction.value; - - if ( op === 'copy' || op === 'move' ) { - // These operations are not supported. - // They are also not used in the mod. - return; - } - - if ( instruction.path === '/-' && op === 'add' ) { - // Special case: the entire data is an array, and we are adding new element to the bottom of it. - data.push( value ); - return; - } - - var objectPath = instruction.path.slice( 1 ).split( '/' ); - if ( op === 'add' ) { - // Handle add operations to: 1) "/some-array/-" (add to the end of array) - // 2) "/some-array/123" (add the new element BEFORE what is currently element #123). - var possiblyIndex = objectPath.pop(); - - if ( possiblyIndex !== '-' && Number.isNaN( Number( possiblyIndex ) ) ) { - // Not an index. We don't consider this an "add to array" operation, - // instead this is "add new key-value" operation. - objectPath.push( possiblyIndex ); - } else { - var currentArray = ( lodash.get( data, objectPath ) || [] ); - - if ( possiblyIndex === '-' ) { - // Pseudo-value "-" means "add after the last element of array". - // Here "value" is the new element that we are adding. - value = currentArray.concat( [ value ] ); - } else { - // Add "value" to currentArray BEFORE currentArray[possiblyIndex] - var before = currentArray.slice( 0, possiblyIndex ), - after = currentArray.slice( possiblyIndex ); - - value = before.concat( [ value ] ).concat( after ); - } - - op = 'replace'; + try { + jsonPatch.applyOperation( data, instruction ); + } catch ( e ) { + if ( e.name === 'TEST_OPERATION_FAILED' ) { + // This "test" instruction told us to skip all instructions after it. + return; } - } - if ( op === 'replace' || op === 'add' ) { - lodash.set( data, objectPath, value ); - } else if ( op === 'remove' ) { - // Carefully delete the element from Array/Object. - // Note: while lodash.unset() would be enough for objects, using it to delete keys from arrays - // would create holes in the array, resulting in incorrect application of further patches. - var index = objectPath.pop(); - var list = lodash.get( data, objectPath ); - - if ( Array.isArray( list ) ) { - // Removing numeric index from array. - list.splice( index, 1 ); - lodash.set( list, objectPath ); - } else { - // Unsetting property of object. - objectPath.push( index ); - lodash.unset( data, objectPath ); - } - } else if ( op === 'test' ) { - var fieldExists = lodash.has( data, objectPath ); - if ( fieldExists && instruction.inverse ) { - // This patch shouldn't be applied if the field exists. - patchSkipped = true; - } else if ( !fieldExists && !instruction.inverse ) { - // This patch shouldn't be applied, because the field doesn't exist. - patchSkipped = true; - } + // FIXME: there are errors in existing assets/patches. + util.log( '[error] Patch error: ' + this.filename + ': ' + e.name + ': ' + JSON.stringify( instruction ) ); } - } ); + } } } diff --git a/package.json b/package.json index 69a73df..b08a43a 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "cli-progress": "^3.12.0", "deepmerge": "^4.3.1", "diff": "^7.0.0", + "fast-json-patch": "^3.1.1", "fs": "^0.0.2", - "lodash": "^4.17.21", "minimist": "^1.2.8", "mwbot": "^2.1.3", "picomatch": "^4.0.2",