Repository: immerjs/immer (js)

Issue:
TypeScript: ReadonlyMap & ReadonlySet not available when targeting `es5`
## 🐛 Bug Report

We had this as a bug report in redux-toolkit, but it boils down to immer.

If the `target` is set to `es5`, the `"ES2015.Collection"` library is not loaded by TypeScript, so `ReadonlyMap` and `ReadonlySet` are not available. This leads to the `Draft` type spinning into an endless recursion (as `ReadonlyMap` is interpreted as the empty object type, matching everything), resulting in a `Draft` type like
```ts
Map<Map<Map<Map<Map<Map<Map<Map<Map<Map<Map<..., ...>, Map<..., ...>>, Map<Map<..., ...>, Map<..., ...>>>, Map<Map<Map<..., ...>, Map<..., ...>>, Map<...>>>, Map<...>>, Map<...>>, Map<...>>, Map<...>>, Map<...>>, Map<...>>, Map<...>>
```
for the most simple drafted objects.

A simple fix would be to not rely on the external definitions of `ReadonlyMap` and `ReadonlySet`, but just to include them, which would mean defining

```ts
declare interface ReaddonlyMap<K, V> {
    forEach(callbackfn: (value: V, key: K, map: ReadonlyMap<K, V>) => void, thisArg?: any): void;
    get(key: K): V | undefined;
    has(key: K): boolean;
    readonly size: number;
}

declare interface ReadonlySet<T> {
    forEach(callbackfn: (value: T, value2: T, set: ReadonlySet<T>) => void, thisArg?: any): void;
    has(value: T): boolean;
    readonly size: number;
}
```

next to the `Draft` type.

If this would be a welcome addition, I'd be happy to open a PR :)

## Link to repro

A very simple reproduction can be found on https://github.com/jon-thompson/rtk-map-issue

## Environment

We only accept bug reports against the latest Immer version.

- **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 description:
skip ReadonlyMap and ReadonlySet types when not available
This should fix #624 .

Instead of my initial idea of providing interfaces with the same shape as `ReadonlyMap`, `ReadonlySet`, `WeakMap` and `WeakSet` (which would also have worked, but probably required a lot more duplicate code), this is now following a different approach:

It checks if the types are `any` (which they would fall back to if they were not present) or a keyless interface (which at least `ReadonlyMap` and `ReadonlySet` would be if the `node` types are installed), in which case they fall back to `void`, which does not trigger the conditional.

The check for `any` is done by `true | false extends (T extends never ? true : false)` -  every other type will either be `true` or `false`, but since `any` takes both paths, it becomes `true | false`.

The check for the empty interface is done by `keyof T extends never` - everything except the empty interface has some keys, including primitives like `string` and `number` (which will have the keys of their prototype).

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 8bb2d09..0be2ef6 100644
--- a/src/plugins/mapset.ts
+++ b/src/plugins/mapset.ts
@@ -15,7 +15,8 @@ import {
 	markChanged,
 	ProxyTypeMap,
 	ProxyTypeSet,
-	die
+	die,
+	each
 } from "../internal"
 
 export function enableMapSet() {
@@ -107,10 +108,15 @@ export function enableMapSet() {
 		p.clear = function() {
 			const state: MapState = this[DRAFT_STATE]
 			assertUnrevoked(state)
-			prepareMapCopy(state)
-			markChanged(state)
-			state.assigned_ = new Map()
-			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(
@@ -269,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/src/types/types-external.ts b/src/types/types-external.ts
index cfb2ca4..0dbd8f8 100644
--- a/src/types/types-external.ts
+++ b/src/types/types-external.ts
@@ -17,30 +17,49 @@ type AtomicObject =
 	| Number
 	| String
 
+/**
+ * If the lib "ES2105.collections" is not included in tsconfig.json,
+ * types like ReadonlyArray, WeakMap etc. fall back to `any` (specified nowhere)
+ * or `{}` (from the node types), in both cases entering an infite recursion in
+ * pattern matching type mappings
+ * This type can be used to cast these types to `void` in these cases.
+ */
+export type IfAvailable<T, Fallback = void> =
+	// fallback if any
+	true | false extends (T extends never
+	? true
+	: false)
+		? Fallback // fallback if empty type
+		: keyof T extends never
+		? Fallback // original type
+		: T
+
 /**
  * These should also never be mapped but must be tested after regular Map and
  * Set
  */
-type WeakReferences = WeakMap<any, any> | WeakSet<any>
+type WeakReferences = IfAvailable<WeakMap<any, any>> | IfAvailable<WeakSet<any>>
+
+export type WritableDraft<T> = {-readonly [K in keyof T]: Draft<T[K]>}
 
 export type Draft<T> = T extends AtomicObject
 	? T
-	: T extends ReadonlyMap<infer K, infer V> // Map extends ReadonlyMap
+	: T extends IfAvailable<ReadonlyMap<infer K, infer V>> // Map extends ReadonlyMap
 	? Map<Draft<K>, Draft<V>>
-	: T extends ReadonlySet<infer V> // Set extends ReadonlySet
+	: T extends IfAvailable<ReadonlySet<infer V>> // Set extends ReadonlySet
 	? Set<Draft<V>>
 	: T extends WeakReferences
 	? T
 	: T extends object
-	? {-readonly [K in keyof T]: Draft<T[K]>}
+	? WritableDraft<T>
 	: T
 
 /** Convert a mutable type into a readonly type */
 export type Immutable<T> = T extends AtomicObject
 	? T
-	: T extends ReadonlyMap<infer K, infer V> // Map extends ReadonlyMap
+	: T extends IfAvailable<ReadonlyMap<infer K, infer V>> // Map extends ReadonlyMap
 	? ReadonlyMap<Immutable<K>, Immutable<V>>
-	: T extends ReadonlySet<infer V> // Set extends ReadonlySet
+	: T extends IfAvailable<ReadonlySet<infer V>> // Set extends ReadonlySet
 	? ReadonlySet<Immutable<V>>
 	: T extends WeakReferences
 	? T
diff --git a/src/utils/common.ts b/src/utils/common.ts
index ab3d262..6798074 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -154,7 +154,7 @@ export function latest(state: ImmerState): any {
 
 /*#__PURE__*/
 export function shallowCopy(base: any) {
-	if (Array.isArray(base)) return base.slice()
+	if (Array.isArray(base)) return Array.prototype.slice.call(base)
 	const descriptors = getOwnPropertyDescriptors(base)
 	delete descriptors[DRAFT_STATE as any]
 	let keys = ownKeys(descriptors)
diff --git a/website/yarn.lock b/website/yarn.lock
index 46f207e..b0dd3ab 100644
--- a/website/yarn.lock
+++ b/website/yarn.lock
@@ -1149,9 +1149,9 @@ binary-extensions@^2.0.0:
   integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
 
 bl@^1.0.0:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
-  integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
+  integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
   dependencies:
     readable-stream "^2.3.5"
     safe-buffer "^5.1.1"
@@ -1265,9 +1265,9 @@ buffer-from@^1.0.0:
   integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
 
 buffer@^5.2.1:
-  version "5.4.3"
-  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115"
-  integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==
+  version "5.6.0"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
+  integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
   dependencies:
     base64-js "^1.0.2"
     ieee754 "^1.1.4"
@@ -1491,9 +1491,9 @@ cli-width@^2.0.0:
   integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
 
 clipboard@^2.0.0:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d"
-  integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
+  integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
   dependencies:
     good-listener "^1.2.2"
     select "^1.1.2"
@@ -1586,12 +1586,10 @@ commander@^2.15.1, commander@^2.20.0:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
   integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
 
-commander@~2.8.1:
-  version "2.8.1"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
-  integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=
-  dependencies:
-    graceful-readlink ">= 1.0.0"
+commander@^2.8.1:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
 
 commondir@^1.0.1:
   version "1.0.1"
@@ -1982,9 +1980,9 @@ decompress-unzip@^4.0.1:
     yauzl "^2.4.2"
 
 decompress@^4.0.0, decompress@^4.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
-  integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
+  integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
   dependencies:
     decompress-tar "^4.0.0"
     decompress-tarbz2 "^4.0.0"
@@ -2268,7 +2266,14 @@ encodeurl@~1.0.2:
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
   integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
 
-end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+end-of-stream@^1.0.0:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+  integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+  dependencies:
+    once "^1.4.0"
+
+end-of-stream@^1.1.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
   integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
@@ -3027,14 +3032,9 @@ got@^8.3.1:
     url-to-options "^1.0.1"
 
 graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
-  integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
-
-"graceful-readlink@>= 1.0.0":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
-  integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
+  version "4.2.4"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
+  integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
 
 gray-matter@^2.1.0:
   version "2.1.1"
@@ -5209,9 +5209,9 @@ prepend-http@^2.0.0:
   integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
 
 prismjs@^1.16.0:
-  version "1.17.1"
-  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be"
-  integrity sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==
+  version "1.21.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3"
+  integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==
   optionalDependencies:
     clipboard "^2.0.0"
 
@@ -5428,7 +5428,7 @@ read-pkg@^1.0.0:
     normalize-package-data "^2.3.2"
     path-type "^1.0.0"
 
-readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
+readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6:
   version "2.3.6"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
   integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
@@ -5441,6 +5441,19 @@ readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable
     string_decoder "~1.1.1"
     util-deprecate "~1.0.1"
 
+readable-stream@^2.3.0, readable-stream@^2.3.5:
+  version "2.3.7"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
 readable-stream@^3.1.1:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
@@ -5688,11 +5701,16 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
-safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
   integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
 
+safe-buffer@^5.1.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
 safe-json-parse@~1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57"
@@ -5724,11 +5742,11 @@ scheduler@^0.15.0:
     object-assign "^4.1.1"
 
 seek-bzip@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
-  integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
+  integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
   dependencies:
-    commander "~2.8.1"
+    commander "^2.8.1"
 
 select@^1.1.2:
   version "1.1.2"
@@ -6455,9 +6473,9 @@ typedarray@^0.0.6:
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
 unbzip2-stream@^1.0.9:
-  version "1.3.3"
-  resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a"
-  integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
+  integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
   dependencies:
     buffer "^5.2.1"
     through "^2.3.8"
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 faa4680..cdcc572 100644
--- a/__tests__/map-set.js
+++ b/__tests__/map-set.js
@@ -244,5 +244,41 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) {
 			expect(map1.has("key")).toBe(true)
 			expect(map1.get("key")).toBe(undefined)
 		})
+
+		test("#663 - clear map", () => {
+			const map = new Map([
+				["a", "b"],
+				["b", "c"]
+			])
+			const result = produceWithPatches(map, draft => {
+				draft.clear()
+			})
+
+			expect(result).toEqual([
+				new Map(),
+				[
+					{op: "remove", path: ["a"]},
+					{op: "remove", path: ["b"]}
+				],
+				[
+					{op: "add", path: ["a"], value: "b"},
+					{op: "add", path: ["b"], value: "c"}
+				]
+			])
+		})
+
+		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)
+		})
 	})
 }
diff --git a/__tests__/regressions.js b/__tests__/regressions.js
index a8e65c7..9ae9138 100644
--- a/__tests__/regressions.js
+++ b/__tests__/regressions.js
@@ -140,5 +140,23 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) {
 			})
 			expect(result).toEqual(new Set())
 		})
+
+		test("#650 - changes with overridden arr.slice() fail", () => {
+			const data = {
+				foo: [
+					{
+						isActive: false
+					}
+				]
+			}
+			// That's roughly what seamless-immutable does
+			data.foo.slice = (...args) =>
+				Object.freeze(Array.prototype.slice.call(data.foo, ...args))
+
+			const newData = produce(data, draft => {
+				draft.foo[0].isActive = true
+			})
+			expect(newData.foo[0].isActive).toBe(true)
+		})
 	})
 }