From 678e54175041b477ee8177e664b89452f717c1e3 Mon Sep 17 00:00:00 2001 From: Xashyar <34171942+Xashyar@users.noreply.github.com> Date: Tue, 17 Nov 2020 13:15:43 +0330 Subject: [PATCH 1/2] chore: Added the missing space in readme.md (#698) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 62153f30..32e917a1 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ Winner of the "Breakthrough of the year" [React open source award](https://osawa ## Contribute using one-click online setup -You can use Gitpod(a free online VS Code like IDE) for contributing online. With a single click it will launch a workspace and automatically: +You can use Gitpod (a free online VS Code like IDE) for contributing online. With a single click it will launch a workspace and automatically: - clone the immer repo. - install the dependencies. From 31684f262a9ec16d5aadb82156a78e81b98f65ef Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Tue, 17 Nov 2020 14:45:08 +0000 Subject: [PATCH 2/2] chore: fix some build issues (#701) * clearer error when plugin is missing * chore: fix travis build not failing, fixes #688 (?) * fix: make plugin loading idempotent, fixes #692 * docs: Organize performance and pitfalls, and document nested produce behavior. Fixes #694 * chore: fix prod-build-issues --- .travis.yml | 11 +- __tests__/__prod_snapshots__/base.js.snap | 80 ++++++-------- __tests__/__prod_snapshots__/patch.js.snap | 6 +- __tests__/__prod_snapshots__/plugins.js.snap | 10 +- __tests__/base.js | 107 ++++++++++--------- __tests__/current.js | 8 +- __tests__/manual.js | 21 ++-- __tests__/map-set.js | 17 ++- __tests__/original.js | 18 +++- docs/performance.md | 18 +++- docs/pitfalls.md | 57 ++++++++-- ignoreObseleteSnapshots.js | 6 ++ jest.config.build.js | 3 +- src/utils/errors.ts | 3 +- src/utils/plugins.ts | 4 +- 15 files changed, 224 insertions(+), 145 deletions(-) create mode 100644 ignoreObseleteSnapshots.js diff --git a/.travis.yml b/.travis.yml index 8d893c73..22a20535 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: - - "10.18.1" - # - "node" + # - "10.18.1" + - "node" env: - NODE_ENV=TEST cache: @@ -12,12 +12,13 @@ before_script: - yarn global add if-node-version script: - yarn build || travis_terminate 1 - - if-node-version 10 || { yarn test && travis_terminate 0; } - - yarn coveralls + - yarn test || travis_terminate 1 + - yarn coveralls || travis_terminate 0 jobs: include: - stage: deploy if: branch == master && !fork script: - - NODE_ENV= yarn build + - yarn test || travis_terminate 1 + - NODE_ENV= yarn build || travis_terminate 1 - yarn semantic-release diff --git a/__tests__/__prod_snapshots__/base.js.snap b/__tests__/__prod_snapshots__/base.js.snap index fa6431a0..4ffc6b65 100644 --- a/__tests__/__prod_snapshots__/base.js.snap +++ b/__tests__/__prod_snapshots__/base.js.snap @@ -2,9 +2,9 @@ exports[`base functionality - es5 (autofreeze) async recipe function works with rejected promises 1`] = `"[Immer] minified error nr: 3 {\\"a\\":0,\\"b\\":1}. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (autofreeze) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -18,19 +18,17 @@ exports[`base functionality - es5 (autofreeze) revokes the draft once produce re exports[`base functionality - es5 (autofreeze) revokes the draft once produce returns 4`] = `"[Immer] minified error nr: 3 [1]. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - es5 (autofreeze) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (autofreeze) throws when the draft is modified and another object is returned 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (autofreeze)(patch listener) async recipe function works with rejected promises 1`] = `"[Immer] minified error nr: 3 {\\"a\\":0,\\"b\\":1}. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze)(patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze)(patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze)(patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze)(patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (autofreeze)(patch listener) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -44,19 +42,17 @@ exports[`base functionality - es5 (autofreeze)(patch listener) revokes the draft exports[`base functionality - es5 (autofreeze)(patch listener) revokes the draft once produce returns 4`] = `"[Immer] minified error nr: 3 [1]. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze)(patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - es5 (autofreeze)(patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze)(patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (autofreeze)(patch listener) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (autofreeze)(patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (autofreeze)(patch listener) throws when the draft is modified and another object is returned 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (no freeze) async recipe function works with rejected promises 1`] = `"[Immer] minified error nr: 3 {\\"a\\":0,\\"b\\":1}. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (no freeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (no freeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (no freeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (no freeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (no freeze) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -70,19 +66,17 @@ exports[`base functionality - es5 (no freeze) revokes the draft once produce ret exports[`base functionality - es5 (no freeze) revokes the draft once produce returns 4`] = `"[Immer] minified error nr: 3 [1]. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (no freeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (no freeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (no freeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - es5 (no freeze) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (no freeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (no freeze) throws when the draft is modified and another object is returned 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (patch listener) async recipe function works with rejected promises 1`] = `"[Immer] minified error nr: 3 {\\"a\\":0,\\"b\\":1}. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (patch listener) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -96,19 +90,17 @@ exports[`base functionality - es5 (patch listener) revokes the draft once produc exports[`base functionality - es5 (patch listener) revokes the draft once produce returns 4`] = `"[Immer] minified error nr: 3 [1]. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - es5 (patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - es5 (patch listener) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - es5 (patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - es5 (patch listener) throws when the draft is modified and another object is returned 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (autofreeze) async recipe function works with rejected promises 1`] = `"Cannot perform 'get' on a proxy that has been revoked"`; -exports[`base functionality - proxy (autofreeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (autofreeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (autofreeze) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -130,11 +122,9 @@ exports[`base functionality - proxy (autofreeze) revokes the draft once produce exports[`base functionality - proxy (autofreeze) revokes the draft once produce returns 8`] = `"Cannot perform 'set' on a proxy that has been revoked"`; -exports[`base functionality - proxy (autofreeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (autofreeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - proxy (autofreeze) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (autofreeze) throws when Object.defineProperty() is used on drafts 1`] = `"[Immer] minified error nr: 11. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -144,9 +134,9 @@ exports[`base functionality - proxy (autofreeze) throws when the draft is modifi exports[`base functionality - proxy (autofreeze)(patch listener) async recipe function works with rejected promises 1`] = `"Cannot perform 'get' on a proxy that has been revoked"`; -exports[`base functionality - proxy (autofreeze)(patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze)(patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (autofreeze)(patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze)(patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (autofreeze)(patch listener) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -168,11 +158,9 @@ exports[`base functionality - proxy (autofreeze)(patch listener) revokes the dra exports[`base functionality - proxy (autofreeze)(patch listener) revokes the draft once produce returns 8`] = `"Cannot perform 'set' on a proxy that has been revoked"`; -exports[`base functionality - proxy (autofreeze)(patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - proxy (autofreeze)(patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze)(patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (autofreeze)(patch listener) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (autofreeze)(patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (autofreeze)(patch listener) throws when Object.defineProperty() is used on drafts 1`] = `"[Immer] minified error nr: 11. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -182,9 +170,9 @@ exports[`base functionality - proxy (autofreeze)(patch listener) throws when the exports[`base functionality - proxy (no freeze) async recipe function works with rejected promises 1`] = `"Cannot perform 'get' on a proxy that has been revoked"`; -exports[`base functionality - proxy (no freeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (no freeze) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (no freeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (no freeze) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (no freeze) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -206,11 +194,9 @@ exports[`base functionality - proxy (no freeze) revokes the draft once produce r exports[`base functionality - proxy (no freeze) revokes the draft once produce returns 8`] = `"Cannot perform 'set' on a proxy that has been revoked"`; -exports[`base functionality - proxy (no freeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (no freeze) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (no freeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - proxy (no freeze) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (no freeze) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (no freeze) throws when Object.defineProperty() is used on drafts 1`] = `"[Immer] minified error nr: 11. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -220,9 +206,9 @@ exports[`base functionality - proxy (no freeze) throws when the draft is modifie exports[`base functionality - proxy (patch listener) async recipe function works with rejected promises 1`] = `"Cannot perform 'get' on a proxy that has been revoked"`; -exports[`base functionality - proxy (patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (patch listener) map drafts revokes map proxies 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (patch listener) map drafts revokes map proxies 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (patch listener) recipe functions cannot return a modified child draft 1`] = `"[Immer] minified error nr: 4. Find the full error at: https://bit.ly/3cXEKWf"`; @@ -244,11 +230,9 @@ exports[`base functionality - proxy (patch listener) revokes the draft once prod exports[`base functionality - proxy (patch listener) revokes the draft once produce returns 8`] = `"Cannot perform 'set' on a proxy that has been revoked"`; -exports[`base functionality - proxy (patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; - -exports[`base functionality - proxy (patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 {}. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (patch listener) set drafts revokes sets 1`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`base functionality - proxy (patch listener) throws on computed properties 1`] = `"[Immer] minified error nr: 1. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`base functionality - proxy (patch listener) set drafts revokes sets 2`] = `"[Immer] minified error nr: 3 '{}'. Find the full error at: https://bit.ly/3cXEKWf"`; exports[`base functionality - proxy (patch listener) throws when Object.defineProperty() is used on drafts 1`] = `"[Immer] minified error nr: 11. Find the full error at: https://bit.ly/3cXEKWf"`; diff --git a/__tests__/__prod_snapshots__/patch.js.snap b/__tests__/__prod_snapshots__/patch.js.snap index 47a33c20..184c445d 100644 --- a/__tests__/__prod_snapshots__/patch.js.snap +++ b/__tests__/__prod_snapshots__/patch.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`applyPatches throws when \`op\` is not "add", "replace", nor "remove" 1`] = `"[Immer] minified error nr: 17 copy. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`applyPatches throws when \`op\` is not "add", "replace", nor "remove" 1`] = `"[Immer] minified error nr: 17 'copy'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`applyPatches throws when \`path\` cannot be resolved 1`] = `"[Immer] minified error nr: 15 a/b. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`applyPatches throws when \`path\` cannot be resolved 1`] = `"[Immer] minified error nr: 15 'a/b'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`applyPatches throws when \`path\` cannot be resolved 2`] = `"[Immer] minified error nr: 15 a/b/c. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`applyPatches throws when \`path\` cannot be resolved 2`] = `"[Immer] minified error nr: 15 'a/b/c'. Find the full error at: https://bit.ly/3cXEKWf"`; diff --git a/__tests__/__prod_snapshots__/plugins.js.snap b/__tests__/__prod_snapshots__/plugins.js.snap index 61bee22e..15975f90 100644 --- a/__tests__/__prod_snapshots__/plugins.js.snap +++ b/__tests__/__prod_snapshots__/plugins.js.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ES5 plugins should throw if no proxies are available error when using ES5 1`] = `"[Immer] minified error nr: 19 ES5. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`ES5 plugins should throw if no proxies are available error when using ES5 1`] = `"[Immer] minified error nr: 18 'ES5'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`error when using Maps 1`] = `"[Immer] minified error nr: 19 MapSet. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`error when using Maps 1`] = `"[Immer] minified error nr: 18 'MapSet'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`error when using patches - 1 1`] = `"[Immer] minified error nr: 19 Patches. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`error when using patches - 1 1`] = `"[Immer] minified error nr: 18 'Patches'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`error when using patches - 2 1`] = `"[Immer] minified error nr: 19 Patches. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`error when using patches - 2 1`] = `"[Immer] minified error nr: 18 'Patches'. Find the full error at: https://bit.ly/3cXEKWf"`; -exports[`error when using patches - 3 1`] = `"[Immer] minified error nr: 19 Patches. Find the full error at: https://bit.ly/3cXEKWf"`; +exports[`error when using patches - 3 1`] = `"[Immer] minified error nr: 18 'Patches'. Find the full error at: https://bit.ly/3cXEKWf"`; diff --git a/__tests__/base.js b/__tests__/base.js index 3382ffa5..29721c1c 100644 --- a/__tests__/base.js +++ b/__tests__/base.js @@ -16,6 +16,8 @@ jest.setTimeout(1000) enableAllPlugins() +const isProd = process.env.NODE_ENV === "production" + test("immer should have no dependencies", () => { expect(require("../package.json").dependencies).toBeUndefined() }) @@ -1145,65 +1147,66 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) { }) // NOTE: ES5 drafts only protect existing properties when revoked. - it("revokes the draft once produce returns", () => { - const expectRevoked = (fn, shouldThrow = true) => { - if (shouldThrow) expect(fn).toThrowErrorMatchingSnapshot() - else expect(fn).not.toThrow() - } + if (!isProd) + it("revokes the draft once produce returns", () => { + const expectRevoked = (fn, shouldThrow = true) => { + if (shouldThrow) expect(fn).toThrowErrorMatchingSnapshot() + else expect(fn).not.toThrow() + } - // Test object drafts: - let draft - produce({a: 1, b: 1}, s => { - draft = s - delete s.b - }) + // Test object drafts: + let draft + produce({a: 1, b: 1}, s => { + draft = s + delete s.b + }) - // Access known property on object draft. - expectRevoked(() => { - draft.a - }) + // Access known property on object draft. + expectRevoked(() => { + draft.a + }) - // Assign known property on object draft. - expectRevoked(() => { - draft.a = true - }) + // Assign known property on object draft. + expectRevoked(() => { + draft.a = true + }) - // Access unknown property on object draft. - expectRevoked(() => { - draft.z - }, useProxies) + // Access unknown property on object draft. + expectRevoked(() => { + draft.z + }, useProxies) - // Assign unknown property on object draft. - expectRevoked(() => { - draft.z = true - }, useProxies) + // Assign unknown property on object draft. + expectRevoked(() => { + draft.z = true + }, useProxies) - // Test array drafts: - produce([1, 2], s => { - draft = s - s.pop() - }) + // Test array drafts: + produce([1, 2], s => { + draft = s + s.pop() + }) - // Access known index of an array draft. - expectRevoked(() => { - draft[0] - }) + // Access known index of an array draft. + expectRevoked(() => { + draft[0] + }) - // Assign known index of an array draft. - expectRevoked(() => { - draft[0] = true - }) + // Assign known index of an array draft. + expectRevoked(() => { + draft[0] = true + }) - // Access unknown index of an array draft. - expectRevoked(() => { - draft[1] - }, useProxies) + // Access unknown index of an array draft. + expectRevoked(() => { + draft[1] + }, useProxies) - // Assign unknown index of an array draft. - expectRevoked(() => { - draft[1] = true - }, useProxies) - }) + // Assign unknown index of an array draft. + expectRevoked(() => { + draft[1] = true + }, useProxies) + }) it("can access a child draft that was created before the draft was modified", () => { produce({a: {}}, s => { @@ -1697,7 +1700,7 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) { }, e => { expect(e).toBe(err) - expect(() => draft.a).toThrowErrorMatchingSnapshot() + if (!isProd) expect(() => draft.a).toThrowErrorMatchingSnapshot() } ) }) @@ -2418,7 +2421,9 @@ function testLiteralTypes(produce) { draft.foo = true }) ).toThrowError( - "produce can only be called on things that are draftable" + isProd + ? "[Immer] minified error nr: 21" + : "produce can only be called on things that are draftable" ) }) } else { diff --git a/__tests__/current.js b/__tests__/current.js index fc1ed18a..add87990 100644 --- a/__tests__/current.js +++ b/__tests__/current.js @@ -14,6 +14,8 @@ enableAllPlugins() runTests("proxy", true) runTests("es5", false) +const isProd = process.env.NODE_ENV === "production" + function runTests(name, useProxies) { describe("current - " + name, () => { beforeAll(() => { @@ -24,7 +26,11 @@ function runTests(name, useProxies) { it("must be called on draft", () => { expect(() => { current({}) - }).toThrowError("[Immer] 'current' expects a draft, got: [object Object]") + }).toThrowError( + isProd + ? "[Immer] minified error nr: 22 '[object Object]'. Find the full error at: https://bit.ly/3cXEKWf" + : "[Immer] 'current' expects a draft, got: [object Object]" + ) }) it("can handle simple arrays", () => { diff --git a/__tests__/manual.js b/__tests__/manual.js index 10d1b768..265f44c0 100644 --- a/__tests__/manual.js +++ b/__tests__/manual.js @@ -10,6 +10,8 @@ import { enableAllPlugins() +const isProd = process.env.NODE_ENV === "production" + runTests("proxy", true) runTests("es5", false) @@ -41,16 +43,17 @@ function runTests(name, useProxies) { expect(state).toEqual([{}, {}, {}]) }) - it("cannot modify after finish", () => { - const state = {a: 1} + if (!isProd) + it("cannot modify after finish", () => { + const state = {a: 1} - const draft = createDraft(state) - draft.a = 2 - expect(finishDraft(draft)).toEqual({a: 2}) - expect(() => { - draft.a = 3 - }).toThrowErrorMatchingSnapshot() - }) + const draft = createDraft(state) + draft.a = 2 + expect(finishDraft(draft)).toEqual({a: 2}) + expect(() => { + draft.a = 3 + }).toThrowErrorMatchingSnapshot() + }) it("should support patches drafts", () => { const state = {a: 1} diff --git a/__tests__/map-set.js b/__tests__/map-set.js index cdcc5725..8bf726f9 100644 --- a/__tests__/map-set.js +++ b/__tests__/map-set.js @@ -5,7 +5,8 @@ import { original, isDraft, immerable, - enableAllPlugins + enableAllPlugins, + enableMapSet } from "../src/immer" import {each, shallowCopy, isEnumerable, DRAFT_STATE} from "../src/common" @@ -280,5 +281,19 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) { }) expect(result).toBe(set) }) + + test("#692 - idempotent plugin loading", () => { + let mapType1 + produce(new Map(), draft => { + mapType1 = draft.constructor + }) + + enableMapSet() + let mapType2 + produce(new Map(), draft => { + mapType2 = draft.constructor + }) + expect(mapType1).toBe(mapType2) + }) }) } diff --git a/__tests__/original.js b/__tests__/original.js index 6d645401..89341c47 100644 --- a/__tests__/original.js +++ b/__tests__/original.js @@ -3,6 +3,8 @@ import produce, {original, setUseProxies, enableAllPlugins} from "../src/immer" enableAllPlugins() +const isProd = process.env.NODE_ENV === "production" + describe("original", () => { const baseState = { a: [], @@ -40,20 +42,28 @@ describe("original", () => { draftState.c = {} draftState.d = 3 expect(() => original(draftState.c)).toThrowErrorMatchingInlineSnapshot( - `"[Immer] 'original' expects a draft, got: [object Object]"` + isProd + ? `"[Immer] minified error nr: 23 '[object Object]'. Find the full error at: https://bit.ly/3cXEKWf"` + : `"[Immer] 'original' expects a draft, got: [object Object]"` ) expect(() => original(draftState.d)).toThrowErrorMatchingInlineSnapshot( - `"[Immer] 'original' expects a draft, got: 3"` + isProd + ? `"[Immer] minified error nr: 23 '3'. Find the full error at: https://bit.ly/3cXEKWf"` + : `"[Immer] 'original' expects a draft, got: 3"` ) }) }) it("should return undefined for an object that is not proxied", () => { expect(() => original({})).toThrowErrorMatchingInlineSnapshot( - `"[Immer] 'original' expects a draft, got: [object Object]"` + isProd + ? `"[Immer] minified error nr: 23 '[object Object]'. Find the full error at: https://bit.ly/3cXEKWf"` + : `"[Immer] 'original' expects a draft, got: [object Object]"` ) expect(() => original(3)).toThrowErrorMatchingInlineSnapshot( - `"[Immer] 'original' expects a draft, got: 3"` + isProd + ? `"[Immer] minified error nr: 23 '3'. Find the full error at: https://bit.ly/3cXEKWf"` + : `"[Immer] 'original' expects a draft, got: 3"` ) }) }) diff --git a/docs/performance.md b/docs/performance.md index 1b0c9ec2..5d8738dd 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -42,6 +42,18 @@ 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 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)})` +### Pre-freeze data + +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. + +### You can always opt-out + +Realize that immer is opt-in everywhere, so it is perfectly fine to manually write super performance critical reducers, and use immer for all the normal ones. Even from within a producer you opt-out from Immer for certain parts of your logic by using utilies `original` or `current` and perform some of your operations on plain JavaScript objects. + +### For expensive search operations, read from the original state, not the draft + +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, alternatively, perform the search on the original value of a draft, by using `original(someDraft)`, which boils to the same thing. + +### Pull produce as far up as possible + +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/docs/pitfalls.md b/docs/pitfalls.md index 480b20b0..22949c8f 100644 --- a/docs/pitfalls.md +++ b/docs/pitfalls.md @@ -7,16 +7,37 @@ title: Pitfalls
-1. For performance tips, see [Performance Tips](https://immerjs.github.io/immer/docs/performance/#performance-tips). -1. Don't redefine draft like, `draft = myCoolNewState`. Instead, either modify the `draft` or return a new state. See [Returning data from producers](https://immerjs.github.io/immer/docs/return). -1. Immer assumes your state to be a unidirectional tree. That is, no object should appear twice in the tree, and there should be no circular references. -1. Since Immer uses proxies, reading huge amounts of data from state comes with an overhead (especially in the ES5 implementation). If this ever becomes an issue (measure before you optimize!), do the current state analysis before entering the producer function or read from the `currentState` rather than the `draftState`. Also, realize that immer is opt-in everywhere, so it is perfectly fine to manually write super performance critical reducers, and use immer for all the normal ones. Also note that `original` can be used to get the original state of an object, which is cheaper to read. -1. It is possible to return values from producers, except, it is not possible to return `undefined` that way, as it is indistinguishable from not updating the draft at all! If you want to replace the draft with `undefined`, just return `nothing` from the producer. -1. Immer [does not support exotic objects](https://github.com/immerjs/immer/issues/504) such as window.location. -1. You will need to enable your own classes to work properly with Immer. For docs on the topic, check out the section on [working with complex objects](https://immerjs.github.io/immer/docs/complex-objects). -1. For arrays, only numeric properties and the `length` property can be mutated. Custom properties are not preserved on arrays. -1. Note that data that comes from the closure, and not from the base state, will never be drafted, even when the data has become part of the new draft: -1. The set of patches generated by Immer should be correct, that is, applying them to an equal base object should result in the same end state. However Immer does not guarantee the generated set of patches will be optimal, that is, the minimum set of patches possible. +### Performance tipes + +For performance tips, see [Performance Tips](https://immerjs.github.io/immer/docs/performance/#performance-tips). + +### Don't reassign the recipe argument + +Never reassign the `draft` argument (example: `draft = myCoolNewState`). Instead, either modify the `draft` or return a new state. See [Returning data from producers](https://immerjs.github.io/immer/docs/return). + +### Immer only supports unidirectional trees + +Immer assumes your state to be a unidirectional tree. That is, no object should appear twice in the tree, there should be no circular references. There should be exactly one path from the root to any node of the tree. + +### Never explicitly return `undefined` from a producer + +It is possible to return values from producers, except, it is not possible to return `undefined` that way, as it is indistinguishable from not updating the draft at all! If you want to replace the draft with `undefined`, just return `nothing` from the producer. + +### Don't mutate exotic objects + +Immer [does not support exotic objects](https://github.com/immerjs/immer/issues/504) such as window.location. + +### Classes should be made draftable or not mutated + +You will need to enable your own classes to work properly with Immer. For docs on the topic, check out the section on [working with complex objects](https://immerjs.github.io/immer/docs/complex-objects). + +### Only valid indices and length can be mutated on Arrays + +For arrays, only numeric properties and the `length` property can be mutated. Custom properties are not preserved on arrays. + +### Data not originating from the state will never be drafted + +Note that data that comes from the closure, and not from the base state, will never be drafted, even when the data has become part of the new draft. ```javascript function onReceiveTodo(todo) { @@ -33,3 +54,19 @@ function onReceiveTodo(todo) { }) } ``` + +### Immer patches are not necessarily optimal + +The set of patches generated by Immer should be correct, that is, applying them to an equal base object should result in the same end state. However Immer does not guarantee the generated set of patches will be optimal, that is, the minimum set of patches possible. + +### Always use the result of nested producers + +Nested `produce` calls are supported, but note that `produce` will _always_ produce a new state, so even when passing a draft to a nested produce, the changes made by the inner produce won't be visibile in the draft that was passed it, but only in the output that is produced. In other words, when using nested produce, you get a draft of a draft and the result of the inner produce should be merged back into the original draft (or returned). For example `produce(state, draft => { produce(draft.user, userDraft => { userDraft.name += "!" })})` won't work as the output if the inner produce isn't used. The correct way to use nested producers is: + +```javascript +produce(state, draft => { + draft.user = produce(draft.user, userDraft => { + userDraft.name += "!" + }) +}) +``` diff --git a/ignoreObseleteSnapshots.js b/ignoreObseleteSnapshots.js new file mode 100644 index 00000000..235de126 --- /dev/null +++ b/ignoreObseleteSnapshots.js @@ -0,0 +1,6 @@ +module.exports = function(results) { + // don't count obselete snapshot as a failure, but just check if there are no failing tests + // console.dir(results) + results.success = results.testResults.every(r => r.numFailingTests === 0) + return results +} diff --git a/jest.config.build.js b/jest.config.build.js index c1c112d9..3cf532b9 100644 --- a/jest.config.build.js +++ b/jest.config.build.js @@ -14,5 +14,6 @@ module.exports = { preset: "ts-jest/presets/js-with-ts", testEnvironment: "node", testMatch: ["**/__tests__/**/*.[jt]s?(x)"], - snapshotResolver: "/jest.config.build.snapshots.js" + snapshotResolver: "/jest.config.build.snapshots.js", + testResultsProcessor: "/ignoreObseleteSnapshots.js" } diff --git a/src/utils/errors.ts b/src/utils/errors.ts index bdb27a30..b73e2aac 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -29,7 +29,6 @@ const errors = { 18(plugin: string) { return `The plugin for '${plugin}' has not been loaded into Immer. To enable the plugin, import and call \`enable${plugin}()\` when initializing your application.` }, - 19: "plugin not loaded", 20: "Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available", 21(thing: string) { return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${thing}'` @@ -54,7 +53,7 @@ export function die(error: keyof typeof errors, ...args: any[]): never { } throw new Error( `[Immer] minified error nr: ${error}${ - args.length ? " " + args.join(",") : "" + args.length ? " " + args.map(s => `'${s}'`).join(",") : "" }. Find the full error at: https://bit.ly/3cXEKWf` ) } diff --git a/src/utils/plugins.ts b/src/utils/plugins.ts index a6f7d3e9..6b2568ab 100644 --- a/src/utils/plugins.ts +++ b/src/utils/plugins.ts @@ -52,7 +52,7 @@ export function getPlugin( ): Exclude { const plugin = plugins[pluginKey] if (!plugin) { - die(__DEV__ ? 18 : 19, pluginKey) + die(18, pluginKey) } // @ts-ignore return plugin @@ -62,7 +62,7 @@ export function loadPlugin( pluginKey: K, implementation: Plugins[K] ): void { - plugins[pluginKey] = implementation + if (!plugins[pluginKey]) plugins[pluginKey] = implementation } /** ES5 Plugin */