Repository: immerjs/immer (js)

Issue:
Clearing empty Set&Map should be noop
## 🐛 Bug Report
## To Reproduce

```
const { produce, enableMapSet } = require("immer");
enableMapSet();
const map = new Map();
const newMap = produce(map, draft => { draft.clear(); });
map === newMap
```
## Observed behavior

false
## Expected behavior

true
## Environment
- **Immer version:**
- [x] I filed this report against the _latest_ version of Immer
- [x] Occurs with `setUseProxies(true)`
- [x] Occurs with `setUseProxies(false)` (ES5 only)


Pull request:
Fix #680: Clearing empty Set&Map should be noop

Patch:
diff --git a/docs/complex-objects.md b/docs/complex-objects.md
index ec92d3f..75369bd 100644
--- a/docs/complex-objects.md
+++ b/docs/complex-objects.md
@@ -37,7 +37,7 @@ class Clock {
 	}
 
 	get time() {
-		return `${this.hour}:${minute}`
+		return `${this.hour}:${this.minute}`
 	}
 
 	tick() {
diff --git a/docs/performance.md b/docs/performance.md
index 5d111aa..1b0c9ec 100644
--- a/docs/performance.md
+++ b/docs/performance.md
@@ -42,6 +42,6 @@ Most important observation:
 
 ## Performance tips
 
-- When adding a large data set to the state tree in an Immer producer (for example data received from a JSON endpoint), it is worth to cool `Object.freeze(json)` on the root of the data to be added first. This will allow Immer to add the new data to the tree faster, as it will skip freezing it, or searching the tree for any changes (drafts) that might be made.
+- When adding a large data set to the state tree in an Immer producer (for example data received from a JSON endpoint), it is worth to call `Object.freeze(json)` on the root of the data to be added first. This will allow Immer to add the new data to the tree faster, as it will skip freezing it, or searching the tree for any changes (drafts) that might be made.
 - Immer will convert anything you read in a draft recursively into a draft as well. If you have expensive side effect free operations on a draft that involves a lot of reading, for example finding an index using `find(Index)` in a very large array, you can speed this up by first doing the search, and only call the `produce` function once you know the index. Thereby preventing Immer to turn everything that was searched for in a draft. Or, perform the search on the original value of a draft, by using `original(someDraft)`, which boils to the same thing.
 - Always try to pull produce 'up', for example `for (let x of y) produce(base, d => d.push(x))` is exponentially slower than `produce(base, d => { for (let x of y) d.push(x)})`
diff --git a/src/plugins/mapset.ts b/src/plugins/mapset.ts
index 6dd579a..0be2ef6 100644
--- a/src/plugins/mapset.ts
+++ b/src/plugins/mapset.ts
@@ -108,13 +108,15 @@ export function enableMapSet() {
 		p.clear = function() {
 			const state: MapState = this[DRAFT_STATE]
 			assertUnrevoked(state)
-			prepareMapCopy(state)
-			markChanged(state)
-			state.assigned_ = new Map()
-			each(state.base_, key => {
-				state.assigned_!.set(key, false)
-			})
-			return state.copy_!.clear()
+			if (latest(state).size) {
+				prepareMapCopy(state)
+				markChanged(state)
+				state.assigned_ = new Map()
+				each(state.base_, key => {
+					state.assigned_!.set(key, false)
+				})
+				state.copy_!.clear()
+			}
 		}
 
 		p.forEach = function(
@@ -273,9 +275,11 @@ export function enableMapSet() {
 		p.clear = function() {
 			const state: SetState = this[DRAFT_STATE]
 			assertUnrevoked(state)
-			prepareSetCopy(state)
-			markChanged(state)
-			return state.copy_!.clear()
+			if (latest(state).size) {
+				prepareSetCopy(state)
+				markChanged(state)
+				state.copy_!.clear()
+			}
 		}
 
 		p.values = function(): IterableIterator<any> {
diff --git a/yarn.lock b/yarn.lock
index 1a22beb..30bb996 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8221,9 +8221,9 @@ node-fetch-npm@^2.0.2:
     safe-buffer "^5.1.1"
 
 node-fetch@^2.3.0:
-  version "2.6.0"
-  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
-  integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
+  integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
 
 node-gyp@^5.0.2, node-gyp@^5.1.0:
   version "5.1.1"
@@ -8476,9 +8476,9 @@ npm-run-path@^4.0.0:
     path-key "^3.0.0"
 
 npm-user-validate@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.0.tgz#8ceca0f5cea04d4e93519ef72d0557a75122e951"
-  integrity sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz#31428fc5475fe8416023f178c0ab47935ad8c561"
+  integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==
 
 npm@^6.10.3:
   version "6.14.7"


Test patch:
diff --git a/__tests__/map-set.js b/__tests__/map-set.js
index bbe9d47..cdcc572 100644
--- a/__tests__/map-set.js
+++ b/__tests__/map-set.js
@@ -266,5 +266,19 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) {
 				]
 			])
 		})
+
+		test("#680 - Clearing empty Set&Map should be noop", () => {
+			const map = new Map()
+			let result = produce(map, draft => {
+				draft.clear()
+			})
+			expect(result).toBe(map)
+
+			const set = new Set()
+			result = produce(set, draft => {
+				draft.clear()
+			})
+			expect(result).toBe(set)
+		})
 	})
 }