Skip to content

Commit

Permalink
feat: added first class support for Map and Set
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Maps and Sets are treated differently now; they will no longer directly mutated when updated inside a draft

This release introduces first class support for Maps and Sets! 
Some things to keep in mind:

* Inside recipes, you can directly modify Maps and Sets with methods like `add`, `set`, `delete` and `clear`
* Those methods do mutate draft Maps and Sets, but won't actually change their originals!
* Immer does not polyfill Map and Set automatically in environments where those aren't available out of the box
* Maps and Sets are supported both in ES5 and Proxy mode
* If `autoFreeze` is enabled, the maps and sets returned from a producer will be artificially frozen by making their mutative APIs unusable
* Non primitive keys for Maps, and non primitive values for Sets are supported. However, we strongly recommend to not combine non-primitive keys to Maps with patches, for reasons expressed below. 

Open questions
* TypeScript support for storing immutable types inside Maps and Sets, and converting them to `Draft`'s, is limited, see #448 for details
* Since JSON-patch standard doesn't offer support for Sets or Maps, it is not entirely clear how mutations to those are best described by patches, so this might be refined in the future. See also #450  

Credits to @runnez, @aigoncharov  and @aleclarson for making this happen!
  • Loading branch information
mweststrate committed Oct 30, 2019
2 parents 09d37ce + 0f4fe3f commit b6f1d40
Show file tree
Hide file tree
Showing 33 changed files with 2,527 additions and 478 deletions.
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ Please provide either a [CodeSandbox demo](https://codesandbox.io/s/82zqr6n3kj)

## Environment

- **Immer version:**
- [ ] Occurs with `setUseProxies(true)`
- [ ] Occurs with `setUseProxies(false)` (ES5 only)
- **Immer version:**
- [ ] Occurs with `setUseProxies(true)`
- [ ] Occurs with `setUseProxies(false)` (ES5 only)
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ typings/

.idea
dist/
website/build
website/build
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"files": [".prettierrc", "*.json"],
"options": {
"printWidth": 200,
"tabWidth": 2
"tabWidth": 2,
"useTabs": false
}
}
]
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ language: node_js
node_js:
- "8"
- "10"
# - "node"
# - "node"
env:
- NODE_ENV=TEST
cache:
Expand All @@ -21,7 +21,7 @@ jobs:
include:
- stage: deploy
if: branch == master && !fork
node_js: "8.12.0" # pre-installed version
node_js: "8.16.0" # pre-installed version
script:
- yarn build
- yarn global add semantic-release@^15
Expand Down
1 change: 1 addition & 0 deletions .watchmanconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
282 changes: 282 additions & 0 deletions __tests__/__snapshots__/base.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,285 @@ exports[`base functionality - proxy (no freeze) throws when Object.definePropert
exports[`base functionality - proxy (no freeze) throws when Object.setPrototypeOf() is used on a draft 1`] = `"Object.setPrototypeOf() cannot be used on an Immer draft"`;

exports[`base functionality - proxy (no freeze) throws when the draft is modified and another object is returned 1`] = `"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft."`;

exports[`complex nesting map / set / object modify deep object 1`] = `
Object {
"map": Map {
"set1" => Set {
Object {
"a": 2,
},
Object {
"b": 2,
},
},
"set2" => Set {
Object {
"c": 3,
},
},
},
}
`;

exports[`complex nesting map / set / object modify deep object 2`] = `
Array [
Object {
"op": "remove",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 1,
},
},
Object {
"op": "add",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 2,
},
},
]
`;

exports[`complex nesting map / set / object modify deep object 3`] = `
Object {
"map": Map {
"set1" => Set {
Object {
"a": 2,
},
Object {
"b": 2,
},
},
"set2" => Set {
Object {
"c": 3,
},
},
},
}
`;

exports[`complex nesting map / set / object modify deep object 4`] = `
Array [
Object {
"op": "remove",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 1,
},
},
Object {
"op": "add",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 2,
},
},
]
`;

exports[`complex nesting map / set / object modify deep object 5`] = `
Object {
"map": Map {
"set1" => Set {
Object {
"a": 2,
},
Object {
"b": 2,
},
},
"set2" => Set {
Object {
"c": 3,
},
},
},
}
`;

exports[`complex nesting map / set / object modify deep object 6`] = `
Array [
Object {
"op": "remove",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 1,
},
},
Object {
"op": "add",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 2,
},
},
]
`;

exports[`complex nesting map / set / object modify deep object 7`] = `
Object {
"map": Map {
"set1" => Set {
Object {
"a": 2,
},
Object {
"b": 2,
},
},
"set2" => Set {
Object {
"c": 3,
},
},
},
}
`;

exports[`complex nesting map / set / object modify deep object 8`] = `
Array [
Object {
"op": "remove",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 1,
},
},
Object {
"op": "add",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 2,
},
},
]
`;

exports[`complex nesting map / set / object modify deep object 9`] = `
Object {
"map": Map {
"set1" => Set {
Object {
"a": 2,
},
Object {
"b": 2,
},
},
"set2" => Set {
Object {
"c": 3,
},
},
},
}
`;

exports[`complex nesting map / set / object modify deep object 10`] = `
Array [
Object {
"op": "remove",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 1,
},
},
Object {
"op": "add",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 2,
},
},
]
`;

exports[`complex nesting map / set / object modify deep object 11`] = `
Object {
"map": Map {
"set1" => Set {
Object {
"a": 2,
},
Object {
"b": 2,
},
},
"set2" => Set {
Object {
"c": 3,
},
},
},
}
`;

exports[`complex nesting map / set / object modify deep object 12`] = `
Array [
Object {
"op": "remove",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 1,
},
},
Object {
"op": "add",
"path": Array [
"map",
"set1",
0,
],
"value": Object {
"a": 2,
},
},
]
`;

0 comments on commit b6f1d40

Please sign in to comment.