diff --git a/package.json b/package.json index 7b25e1da310..c7580abce8a 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,11 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "sideEffects": [ - "./dist/cache/inmemory/fixPolyfills.js" + "./dist/cache/inmemory/fixPolyfills.native.js" ], + "react-native": { + "./dist/cache/inmemory/fixPolyfills.js": "./dist/cache/inmemory/fixPolyfills.native.js" + }, "repository": { "type": "git", "url": "git+https://github.com/apollographql/apollo-client.git" @@ -48,7 +51,7 @@ { "name": "apollo-client", "path": "./dist/apollo-client.cjs.min.js", - "maxSize": "24.2 kB" + "maxSize": "24 kB" } ], "peerDependencies": { diff --git a/src/cache/inmemory/fixPolyfills.native.ts b/src/cache/inmemory/fixPolyfills.native.ts new file mode 100644 index 00000000000..98b55ae5419 --- /dev/null +++ b/src/cache/inmemory/fixPolyfills.native.ts @@ -0,0 +1,53 @@ +// Make sure Map.prototype.set returns the Map instance, per spec. +// https://github.com/apollographql/apollo-client/issues/4024 +const testMap = new Map(); +if (testMap.set(1, 2) !== testMap) { + const { set } = testMap; + Map.prototype.set = function (...args) { + set.apply(this, args); + return this; + }; +} + +// Make sure Set.prototype.add returns the Set instance, per spec. +const testSet = new Set(); +if (testSet.add(3) !== testSet) { + const { add } = testSet; + Set.prototype.add = function (...args) { + add.apply(this, args); + return this; + }; +} + +const frozen = {}; +if (typeof Object.freeze === 'function') { + Object.freeze(frozen); +} + +try { + // If non-extensible objects can't be stored as keys in a Map, make sure we + // do not freeze/seal/etc. an object without first attempting to put it in a + // Map. For example, this gives the React Native Map polyfill a chance to tag + // objects before they become non-extensible: + // https://github.com/facebook/react-native/blob/98a6f19d7c/Libraries/vendor/core/Map.js#L44-L50 + // https://github.com/apollographql/react-apollo/issues/2442#issuecomment-426489517 + testMap.set(frozen, frozen).delete(frozen); +} catch { + const wrap = (method: (obj: T) => T): typeof method => { + return method && (obj => { + try { + // If .set succeeds, also call .delete to avoid leaking memory. + testMap.set(obj, obj).delete(obj); + } finally { + // If .set or .delete fails, the exception will be silently swallowed + // by this return-from-finally statement: + return method.call(Object, obj); + } + }); + }; + Object.freeze = wrap(Object.freeze); + Object.seal = wrap(Object.seal); + Object.preventExtensions = wrap(Object.preventExtensions); +} + +export {} diff --git a/src/cache/inmemory/fixPolyfills.ts b/src/cache/inmemory/fixPolyfills.ts index 98b55ae5419..4fbe4fd9965 100644 --- a/src/cache/inmemory/fixPolyfills.ts +++ b/src/cache/inmemory/fixPolyfills.ts @@ -1,53 +1,7 @@ -// Make sure Map.prototype.set returns the Map instance, per spec. -// https://github.com/apollographql/apollo-client/issues/4024 -const testMap = new Map(); -if (testMap.set(1, 2) !== testMap) { - const { set } = testMap; - Map.prototype.set = function (...args) { - set.apply(this, args); - return this; - }; -} - -// Make sure Set.prototype.add returns the Set instance, per spec. -const testSet = new Set(); -if (testSet.add(3) !== testSet) { - const { add } = testSet; - Set.prototype.add = function (...args) { - add.apply(this, args); - return this; - }; -} - -const frozen = {}; -if (typeof Object.freeze === 'function') { - Object.freeze(frozen); -} - -try { - // If non-extensible objects can't be stored as keys in a Map, make sure we - // do not freeze/seal/etc. an object without first attempting to put it in a - // Map. For example, this gives the React Native Map polyfill a chance to tag - // objects before they become non-extensible: - // https://github.com/facebook/react-native/blob/98a6f19d7c/Libraries/vendor/core/Map.js#L44-L50 - // https://github.com/apollographql/react-apollo/issues/2442#issuecomment-426489517 - testMap.set(frozen, frozen).delete(frozen); -} catch { - const wrap = (method: (obj: T) => T): typeof method => { - return method && (obj => { - try { - // If .set succeeds, also call .delete to avoid leaking memory. - testMap.set(obj, obj).delete(obj); - } finally { - // If .set or .delete fails, the exception will be silently swallowed - // by this return-from-finally statement: - return method.call(Object, obj); - } - }); - }; - Object.freeze = wrap(Object.freeze); - Object.seal = wrap(Object.seal); - Object.preventExtensions = wrap(Object.preventExtensions); -} - -export {} +// Most JavaScript environments do not need the workarounds implemented in +// fixPolyfills.native.ts, so importing fixPolyfills.ts merely imports +// this empty module, adding nothing to bundle sizes or execution times. +// When bundling for React Native, we substitute fixPolyfills.native.js +// for fixPolyfills.js (see the "react-native" section of package.json), +// to work around problems with Map and Set polyfills in older versions of +// React Native (which should have been fixed in react-native@0.59.0).