From 65ca3fe12f222b2c258c4f0f509ff039c24b623b Mon Sep 17 00:00:00 2001
From: queicherius <queicherius@gmail.com>
Date: Thu, 14 Nov 2024 05:24:48 +0000
Subject: [PATCH] Remove mutating objects

---
 src/cheapestTree.ts        | 19 ++++++++++++-------
 tests/cheapestTree.spec.ts | 15 +++------------
 2 files changed, 15 insertions(+), 19 deletions(-)

diff --git a/src/cheapestTree.ts b/src/cheapestTree.ts
index f50f713..48a0d05 100755
--- a/src/cheapestTree.ts
+++ b/src/cheapestTree.ts
@@ -16,7 +16,6 @@ export function cheapestTree(
   forceBuyItems: Array<number> = [],
   valueOwnItems = false
 ): RecipeTreeWithCraftFlags {
-  // calculateTreeQuantity already checks for craft flags, so we can set them here when valuing owned items
   if (valueOwnItems) {
     const treeWithQuantityWithoutAvailableItems = calculateTreeQuantity(
       amount,
@@ -27,18 +26,22 @@ export function cheapestTree(
       treeWithQuantityWithoutAvailableItems,
       itemPrices
     )
+
     const cheaperToBuyItemIds = getCheaperToBuyItemIds(treeWithPriceWithoutAvailableItems)
-    disableCraftForItemIds(tree, cheaperToBuyItemIds)
+
+    // calculateTreeQuantity already checks for craft flags, so we can set them here
+    tree = disableCraftForItemIds(tree, cheaperToBuyItemIds) as NestedRecipe
   }
+
   // Adjust the tree total and used quantities
   const treeWithQuantity = calculateTreeQuantity(amount, tree as RecipeTree, availableItems)
 
   // Set the initial craft flags based on the subtree prices
   const treeWithPrices = calculateTreePrices(treeWithQuantity, itemPrices)
-  const treeWithCraftFlags = calculateTreeCraftFlags(treeWithPrices, forceBuyItems)
+  let treeWithCraftFlags = calculateTreeCraftFlags(treeWithPrices, forceBuyItems)
 
   // Force the root to be crafted
-  treeWithCraftFlags.craft = true
+  treeWithCraftFlags = { ...treeWithCraftFlags, craft: true }
 
   // After the "craft" flags are set, update the used materials
   // to only be used for things that actually get crafted
@@ -87,12 +90,14 @@ function disableCraftForItemIds(
   ids: Array<number>
 ) {
   if (ids.includes(tree.id)) {
-    tree.craft = false
+    tree = { ...tree, craft: false }
   }
 
   if ('components' in tree && Array.isArray(tree.components)) {
-    tree.components.forEach((component) => {
+    tree.components = tree.components.map((component) =>
       disableCraftForItemIds(component as NestedRecipeAndBasicComponentWithCraftFlag, ids)
-    })
+    )
   }
+
+  return tree
 }
diff --git a/tests/cheapestTree.spec.ts b/tests/cheapestTree.spec.ts
index 7ffbb54..98c3994 100755
--- a/tests/cheapestTree.spec.ts
+++ b/tests/cheapestTree.spec.ts
@@ -1,8 +1,6 @@
 import { NestedRecipe } from '@gw2efficiency/recipe-nesting'
 import { cheapestTree } from '../src'
 import { RecipeTreeWithCraftFlags } from '../src/types'
-import { clone } from '@devoxa/flocky'
-import { JSONValue } from '@devoxa/flocky/dist/typeHelpers'
 
 type SimplifiedTree = {
   id: number
@@ -18,10 +16,6 @@ function simplifyTree(tree: RecipeTreeWithCraftFlags): SimplifiedTree {
   }
 }
 
-function cloneRecipe(recipe: NestedRecipe): NestedRecipe {
-  return clone(recipe as unknown as JSONValue) as unknown as NestedRecipe
-}
-
 describe('cheapestTree', () => {
   it('can calculate the cheapest tree correctly', () => {
     const recipeTree: NestedRecipe = {
@@ -180,20 +174,17 @@ describe('cheapestTree', () => {
     const prices = { 1: 10, 3: 100, 4: 500 }
     const availableItems = { 3: 4, 4: 100 }
 
-    const recipeTreeA = cloneRecipe(recipeTree)
-    const calculatedTreeNoOwn = cheapestTree(1, recipeTreeA, prices)
+    const calculatedTreeNoOwn = cheapestTree(1, recipeTree, prices)
     const simpleTreeNoOwn = simplifyTree(calculatedTreeNoOwn)
 
     // Should make the same decisions as if we had no own materials
-    const recipeTreeB = cloneRecipe(recipeTree)
-    const calculatedTree = cheapestTree(1, recipeTreeB, prices, availableItems, [], true)
+    const calculatedTree = cheapestTree(1, recipeTree, prices, availableItems, [], true)
     const simpleTree = simplifyTree(calculatedTree)
     expect(calculatedTree).toMatchSnapshot()
     expect(simpleTree).toEqual(simpleTreeNoOwn)
 
     // Should value own materials as "free" and make other decisions
-    const recipeTreeC = cloneRecipe(recipeTree)
-    const calculatedTreeNoValue = cheapestTree(1, recipeTreeC, prices, availableItems, [], false)
+    const calculatedTreeNoValue = cheapestTree(1, recipeTree, prices, availableItems, [], false)
     const simpleTreeNoValue = simplifyTree(calculatedTreeNoValue)
     expect(simpleTree).not.toEqual(simpleTreeNoValue)
   })