diff --git a/node_modules/just-diff/index.cjs b/node_modules/just-diff/index.cjs index 11ad3710b98f4..5f4fa3400b207 100644 --- a/node_modules/just-diff/index.cjs +++ b/node_modules/just-diff/index.cjs @@ -79,18 +79,32 @@ function diff(obj1, obj2, pathConverter) { return arr; }); - function getDiff(obj1, obj2, basePath, diffs) { + // we will gather all permutations and return the one with the fewest diffs + var permutations = [{remove: [], replace: [], add: []}]; + + function getDiff({obj1, obj2, basePath, basePathForRemoves, permutation}) { var obj1Keys = Object.keys(obj1); var obj1KeysLength = obj1Keys.length; var obj2Keys = Object.keys(obj2); var obj2KeysLength = obj2Keys.length; var path; + var newPermutation; + + var lengthDelta = obj1.length - obj2.length; + // if both objects are arrays and obj1 length > obj2 length + // we create an additional permutation that trims obj1 from left + if (Array.isArray(obj1) && Array.isArray(obj2) && lengthDelta > 0) { + newPermutation = clonePermutation(permutation); + permutations.push(newPermutation); + } + + // trim from right for (var i = 0; i < obj1KeysLength; i++) { var key = Array.isArray(obj1) ? Number(obj1Keys[i]) : obj1Keys[i]; if (!(key in obj2)) { - path = basePath.concat(key); - diffs.remove.push({ + path = basePathForRemoves.concat(key); + permutation.remove.push({ op: 'remove', path: pathConverter(path), }); @@ -99,55 +113,120 @@ function diff(obj1, obj2, pathConverter) { for (var i = 0; i < obj2KeysLength; i++) { var key = Array.isArray(obj2) ? Number(obj2Keys[i]) : obj2Keys[i]; - var obj1AtKey = obj1[key]; - var obj2AtKey = obj2[key]; - if (!(key in obj1)) { - path = basePath.concat(key); - var obj2Value = obj2[key]; - diffs.add.push({ - op: 'add', + pushReplaces({ + key, + obj1, + obj2, + path: basePath.concat(key), + pathForRemoves: basePath.concat(key), + permutation, + }); + } + + // if we created a new permutation above it means we should also try trimming from left + if (newPermutation) { + for (var i = 0; i < lengthDelta; i++) { + path = basePathForRemoves.concat(i); + newPermutation.remove.push({ + op: 'remove', path: pathConverter(path), - value: obj2Value, }); - } else if (obj1AtKey !== obj2AtKey) { - if ( - Object(obj1AtKey) !== obj1AtKey || - Object(obj2AtKey) !== obj2AtKey - ) { - path = pushReplace(path, basePath, key, diffs, pathConverter, obj2); - } else { - if ( - !Object.keys(obj1AtKey).length && - !Object.keys(obj2AtKey).length && - String(obj1AtKey) != String(obj2AtKey) - ) { - path = pushReplace(path, basePath, key, diffs, pathConverter, obj2); - } else { - getDiff(obj1[key], obj2[key], basePath.concat(key), diffs); - } - } } - } - return diffs; + // now make a copy of obj1 with excess elements left trimmed and see if there any replaces + var obj1Trimmed = obj1.slice(lengthDelta);; + for (var i = 0; i < obj2KeysLength; i++) { + pushReplaces({ + key: i, + obj1: obj1Trimmed, + obj2, + path: basePath.concat(i), + // since list of removes are reversed before presenting result, + // we need to ignore existing parent removes when doing nested removes + pathForRemoves: basePath.concat(i + lengthDelta), + permutation: newPermutation, + }); + } + } } - const finalDiffs = getDiff(obj1, obj2, [], {remove: [], replace: [], add: []}); + + getDiff({ + obj1, + obj2, + basePath: [], + basePathForRemoves: [], + permutation: permutations[0], + }); + + // find the shortest permutation + var finalDiffs = permutations.sort( + (a, b) => diffStepCount(a) > diffStepCount(b) ? 1 : -1 + )[0]; + + // reverse removes since we want to maintain indexes return finalDiffs.remove .reverse() .concat(finalDiffs.replace) .concat(finalDiffs.add); + + function pushReplaces({key, obj1, obj2, path, pathForRemoves, permutation}) { + var obj1AtKey = obj1[key]; + var obj2AtKey = obj2[key]; + + if(!(key in obj1) && obj2AtKey) { + var obj2Value = obj2AtKey; + permutation.add.push({ + op: 'add', + path: pathConverter(path), + value: obj2Value, + }); + } else if(obj1AtKey !== obj2AtKey) { + if(Object(obj1AtKey) !== obj1AtKey || + Object(obj2AtKey) !== obj2AtKey || differentTypes(obj1AtKey, obj2AtKey) + ) { + pushReplace(path, permutation, obj2AtKey); + } else { + if(!Object.keys(obj1AtKey).length && + !Object.keys(obj2AtKey).length && + String(obj1AtKey) != String(obj2AtKey)) { + pushReplace(path, permutation, obj2AtKey); + } else { + getDiff({ + obj1: obj1[key], + obj2: obj2[key], + basePath: path, + basePathForRemoves: pathForRemoves, + permutation}); + } + } + } + } + + function pushReplace(path, diffs, newValue) { + diffs.replace.push({ + op: 'replace', + path: pathConverter(path), + value: newValue, + }); + } } -function pushReplace(path, basePath, key, diffs, pathConverter, obj2) { - path = basePath.concat(key); - diffs.replace.push({ - op: 'replace', - path: pathConverter(path), - value: obj2[key], - }); - return path; +function clonePermutation(permutation) { + return { + remove: permutation.remove.slice(0), + replace: permutation.replace.slice(0), + add: permutation.add.slice(0), + }; +} + +function diffStepCount(permutation) { + return permutation.remove.length + permutation.replace.length + permutation.add.length; } function jsonPatchPathConverter(arrayPath) { return [''].concat(arrayPath).join('/'); } + +function differentTypes(a, b) { + return Object.prototype.toString.call(a) != Object.prototype.toString.call(b); +} diff --git a/node_modules/just-diff/index.mjs b/node_modules/just-diff/index.mjs index a0c5834475fea..7c32c57f50701 100644 --- a/node_modules/just-diff/index.mjs +++ b/node_modules/just-diff/index.mjs @@ -74,18 +74,32 @@ function diff(obj1, obj2, pathConverter) { return arr; }); - function getDiff(obj1, obj2, basePath, diffs) { + // we will gather all permutations and return the one with the fewest diffs + var permutations = [{remove: [], replace: [], add: []}]; + + function getDiff({obj1, obj2, basePath, basePathForRemoves, permutation}) { var obj1Keys = Object.keys(obj1); var obj1KeysLength = obj1Keys.length; var obj2Keys = Object.keys(obj2); var obj2KeysLength = obj2Keys.length; var path; + var newPermutation; + + var lengthDelta = obj1.length - obj2.length; + // if both objects are arrays and obj1 length > obj2 length + // we create an additional permutation that trims obj1 from left + if (Array.isArray(obj1) && Array.isArray(obj2) && lengthDelta > 0) { + newPermutation = clonePermutation(permutation); + permutations.push(newPermutation); + } + + // trim from right for (var i = 0; i < obj1KeysLength; i++) { var key = Array.isArray(obj1) ? Number(obj1Keys[i]) : obj1Keys[i]; if (!(key in obj2)) { - path = basePath.concat(key); - diffs.remove.push({ + path = basePathForRemoves.concat(key); + permutation.remove.push({ op: 'remove', path: pathConverter(path), }); @@ -94,57 +108,121 @@ function diff(obj1, obj2, pathConverter) { for (var i = 0; i < obj2KeysLength; i++) { var key = Array.isArray(obj2) ? Number(obj2Keys[i]) : obj2Keys[i]; - var obj1AtKey = obj1[key]; - var obj2AtKey = obj2[key]; - if (!(key in obj1)) { - path = basePath.concat(key); - var obj2Value = obj2[key]; - diffs.add.push({ - op: 'add', + pushReplaces({ + key, + obj1, + obj2, + path: basePath.concat(key), + pathForRemoves: basePath.concat(key), + permutation, + }); + } + + // if we created a new permutation above it means we should also try trimming from left + if (newPermutation) { + for (var i = 0; i < lengthDelta; i++) { + path = basePathForRemoves.concat(i); + newPermutation.remove.push({ + op: 'remove', path: pathConverter(path), - value: obj2Value, }); - } else if (obj1AtKey !== obj2AtKey) { - if ( - Object(obj1AtKey) !== obj1AtKey || - Object(obj2AtKey) !== obj2AtKey - ) { - path = pushReplace(path, basePath, key, diffs, pathConverter, obj2); - } else { - if ( - !Object.keys(obj1AtKey).length && - !Object.keys(obj2AtKey).length && - String(obj1AtKey) != String(obj2AtKey) - ) { - path = pushReplace(path, basePath, key, diffs, pathConverter, obj2); - } else { - getDiff(obj1[key], obj2[key], basePath.concat(key), diffs); - } - } } - } - return diffs; + // now make a copy of obj1 with excess elements left trimmed and see if there any replaces + var obj1Trimmed = obj1.slice(lengthDelta); for (var i = 0; i < obj2KeysLength; i++) { + pushReplaces({ + key: i, + obj1: obj1Trimmed, + obj2, + path: basePath.concat(i), + // since list of removes are reversed before presenting result, + // we need to ignore existing parent removes when doing nested removes + pathForRemoves: basePath.concat(i + lengthDelta), + permutation: newPermutation, + }); + } + } } - const finalDiffs = getDiff(obj1, obj2, [], {remove: [], replace: [], add: []}); + + getDiff({ + obj1, + obj2, + basePath: [], + basePathForRemoves: [], + permutation: permutations[0], + }); + + // find the shortest permutation + var finalDiffs = permutations.sort( + (a, b) => diffStepCount(a) > diffStepCount(b) ? 1 : -1 + )[0]; + + // reverse removes since we want to maintain indexes return finalDiffs.remove .reverse() .concat(finalDiffs.replace) .concat(finalDiffs.add); + + function pushReplaces({key, obj1, obj2, path, pathForRemoves, permutation}) { + var obj1AtKey = obj1[key]; + var obj2AtKey = obj2[key]; + + if(!(key in obj1) && obj2AtKey) { + var obj2Value = obj2AtKey; + permutation.add.push({ + op: 'add', + path: pathConverter(path), + value: obj2Value, + }); + } else if(obj1AtKey !== obj2AtKey) { + if(Object(obj1AtKey) !== obj1AtKey || + Object(obj2AtKey) !== obj2AtKey || differentTypes(obj1AtKey, obj2AtKey) + ) { + pushReplace(path, permutation, obj2AtKey); + } else { + if(!Object.keys(obj1AtKey).length && + !Object.keys(obj2AtKey).length && + String(obj1AtKey) != String(obj2AtKey)) { + pushReplace(path, permutation, obj2AtKey); + } else { + getDiff({ + obj1: obj1[key], + obj2: obj2[key], + basePath: path, + basePathForRemoves: pathForRemoves, + permutation}); + } + } + } + } + + function pushReplace(path, diffs, newValue) { + diffs.replace.push({ + op: 'replace', + path: pathConverter(path), + value: newValue, + }); + } } -function pushReplace(path, basePath, key, diffs, pathConverter, obj2) { - path = basePath.concat(key); - diffs.replace.push({ - op: 'replace', - path: pathConverter(path), - value: obj2[key], - }); - return path; +function clonePermutation(permutation) { + return { + remove: permutation.remove.slice(0), + replace: permutation.replace.slice(0), + add: permutation.add.slice(0), + }; +} + +function diffStepCount(permutation) { + return permutation.remove.length + permutation.replace.length + permutation.add.length; } function jsonPatchPathConverter(arrayPath) { return [''].concat(arrayPath).join('/'); } +function differentTypes(a, b) { + return Object.prototype.toString.call(a) != Object.prototype.toString.call(b); +} + export {diff, jsonPatchPathConverter}; diff --git a/node_modules/just-diff/package.json b/node_modules/just-diff/package.json index 4456df5db5215..f0def5fa12dff 100644 --- a/node_modules/just-diff/package.json +++ b/node_modules/just-diff/package.json @@ -1,6 +1,6 @@ { "name": "just-diff", - "version": "5.2.0", + "version": "6.0.0", "description": "Return an object representing the diffs between two objects. Supports jsonPatch protocol", "type": "module", "exports": { diff --git a/node_modules/parse-conflict-json/package.json b/node_modules/parse-conflict-json/package.json index 7b86df89edb39..32584d3e6401b 100644 --- a/node_modules/parse-conflict-json/package.json +++ b/node_modules/parse-conflict-json/package.json @@ -1,6 +1,6 @@ { "name": "parse-conflict-json", - "version": "3.0.0", + "version": "3.0.1", "description": "Parse a JSON string that has git merge conflicts, resolving if possible", "author": "GitHub Inc.", "license": "ISC", @@ -22,13 +22,13 @@ ] }, "devDependencies": { - "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "4.5.1", + "@npmcli/eslint-config": "^4.0.0", + "@npmcli/template-oss": "4.12.0", "tap": "^16.0.1" }, "dependencies": { "json-parse-even-better-errors": "^3.0.0", - "just-diff": "^5.0.1", + "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "repository": { @@ -44,6 +44,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.5.1" + "version": "4.12.0" } } diff --git a/package-lock.json b/package-lock.json index b13ef4e721501..309622d373dbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,7 +134,7 @@ "npmlog": "^7.0.1", "p-map": "^4.0.0", "pacote": "^15.1.1", - "parse-conflict-json": "^3.0.0", + "parse-conflict-json": "^3.0.1", "proc-log": "^3.0.0", "qrcode-terminal": "^0.12.0", "read": "^2.0.0", @@ -2328,6 +2328,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@npmcli/template-oss/node_modules/just-diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.2.0.tgz", + "integrity": "sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw==", + "dev": true + }, "node_modules/@octokit/auth-token": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz", @@ -7165,9 +7171,9 @@ "dev": true }, "node_modules/just-diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.2.0.tgz", - "integrity": "sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.0.tgz", + "integrity": "sha512-MbEkhMEa9p7bVD/U29cTM6zUHcLPWwSZjnSquOu++6GyKfoc3aGNdsp2uLQ3Zq0ocg60MzTxwB9qjqwgoQu3+g==", "inBundle": true }, "node_modules/just-diff-apply": { @@ -10004,13 +10010,13 @@ } }, "node_modules/parse-conflict-json": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.0.tgz", - "integrity": "sha512-ipcKLCmZbAj7n+h9qQREvdvsBUMPetGk9mM4ljCvs5inZznAlkHPk5XPc7ROtknUKw7kO6Jnz10Y3Eec7tky/A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz", + "integrity": "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==", "inBundle": true, "dependencies": { "json-parse-even-better-errors": "^3.0.0", - "just-diff": "^5.0.1", + "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { diff --git a/package.json b/package.json index 5c3b15b77ab32..b71d426aab43c 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "npmlog": "^7.0.1", "p-map": "^4.0.0", "pacote": "^15.1.1", - "parse-conflict-json": "^3.0.0", + "parse-conflict-json": "^3.0.1", "proc-log": "^3.0.0", "qrcode-terminal": "^0.12.0", "read": "^2.0.0",