From a7232bf7d93085cd820f6db1a7688393aca68581 Mon Sep 17 00:00:00 2001 From: hwillson Date: Fri, 29 May 2020 15:43:21 -0400 Subject: [PATCH 1/2] Replace requireReactLazily with CJS bundle manipulation PR #5577 introduced a new way of lazily loading React to help prevent modern bundlers from requiring React when used with `@apollo/client/core` (in other words, without any of Apollo Client's React components). While this approach works well for applications that aren't using React, it introduces problems for bundlers and applications that are using React (as outlined in #6035 and #6352). There are several different ways we can address this, and we might do something more substantial in the future, but for now this commit manipulates Apollo Client's core CJS bundle at build time, to make the React require optional. Fixes #6035. Fixes #6352. --- config/rollup.config.js | 31 ++++ .../rollup-ac3-no-react/package-lock.json | 16 +- .../rollup-ac3/package-lock.json | 16 +- .../rollup-ac3-no-react/package-lock.json | 16 +- .../tree-shaking/rollup-ac3/package-lock.json | 147 +++++++++++------- .../tree-shaking/rollup-ac3/package.json | 4 +- src/react/context/ApolloConsumer.tsx | 3 +- src/react/context/ApolloContext.ts | 4 +- src/react/context/ApolloProvider.tsx | 3 +- .../context/__tests__/ApolloConsumer.test.tsx | 4 +- .../context/__tests__/ApolloProvider.test.tsx | 5 +- .../hooks/__tests__/useApolloClient.test.tsx | 4 +- .../hooks/__tests__/useLazyQuery.test.tsx | 4 +- .../hooks/__tests__/useMutation.test.tsx | 5 +- src/react/hooks/__tests__/useQuery.test.tsx | 5 +- .../hooks/__tests__/useSubscription.test.tsx | 4 +- src/react/hooks/useApolloClient.ts | 3 +- src/react/hooks/useMutation.ts | 3 +- src/react/hooks/useSubscription.ts | 4 +- src/react/hooks/utils/useBaseQuery.ts | 4 +- src/react/hooks/utils/useDeepMemo.ts | 5 +- src/react/react.ts | 9 -- 22 files changed, 166 insertions(+), 133 deletions(-) delete mode 100644 src/react/react.ts diff --git a/config/rollup.config.js b/config/rollup.config.js index 5c88d7a551b..429b891d76c 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -63,6 +63,37 @@ function prepareCJS(input, output) { }, plugins: [ nodeResolve(), + // When generating the `dist/core/core.cjs.js` entry point (in + // `config/prepareDist.js`), we filter and re-export the exports we + // need from the main Apollo Client CJS bundle (to exclude React related + // code). This means that consumers of `core.cjs.js` attempt to load the + // full AC CJS bundle first (before filtering exports), which then means + // the React require in the AC CJS bundle is attempted and not found + // (since people using `core.cjs.js` want to use Apollo Client without + // React). To address this, we make React an optional require in the CJS + // bundle. + (() => { + const cjsBundle = output.replace(`${distDir}/`, ''); + return { + generateBundle(_option, bundle) { + const { code } = bundle[cjsBundle]; + const regex = /var React = require\('react'\);/; + const matches = code.match(regex); + if (matches && matches.length === 1) { + bundle[cjsBundle].code = + code.replace( + regex, + "try { var React = require('react'); } catch (error) {}" + ); + } else { + throw new Error( + 'The CJS bundle could not prepared as a single React ' + + 'require could not be found.' + ); + } + } + } + })() ], }; } diff --git a/examples/bundling/no-tree-shaking/rollup-ac3-no-react/package-lock.json b/examples/bundling/no-tree-shaking/rollup-ac3-no-react/package-lock.json index 9a08b660d75..057a95dfbda 100644 --- a/examples/bundling/no-tree-shaking/rollup-ac3-no-react/package-lock.json +++ b/examples/bundling/no-tree-shaking/rollup-ac3-no-react/package-lock.json @@ -11,7 +11,7 @@ "@wry/equality": "^0.1.9", "fast-json-stable-stringify": "^2.0.0", "graphql-tag": "^2.10.2", - "optimism": "^0.11.5", + "optimism": "^0.12.1", "symbol-observable": "^1.2.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0", @@ -1172,9 +1172,9 @@ "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" }, "@wry/context": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.0.tgz", - "integrity": "sha512-yW5XFrWbRvv2/f86+g0YAfko7671SQg7Epn8lYjux4MZmIvtPvK2ts+vG1QAPu2w6GuBioEJknRa7K2LFj2kQw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.2.tgz", + "integrity": "sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==", "requires": { "tslib": "^1.9.3" } @@ -5827,11 +5827,11 @@ } }, "optimism": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.5.tgz", - "integrity": "sha512-twCHmBb64DYzEZ8A3O+TLCuF/RmZPBhXPQYv4agoiALRLlW9SidMzd7lwUP9mL0jOZhzhnBmb8ajqA00ECo/7g==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.12.1.tgz", + "integrity": "sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ==", "requires": { - "@wry/context": "^0.5.0" + "@wry/context": "^0.5.2" } }, "optimist": { diff --git a/examples/bundling/no-tree-shaking/rollup-ac3/package-lock.json b/examples/bundling/no-tree-shaking/rollup-ac3/package-lock.json index b1f7526df6a..8ee6c17dabf 100644 --- a/examples/bundling/no-tree-shaking/rollup-ac3/package-lock.json +++ b/examples/bundling/no-tree-shaking/rollup-ac3/package-lock.json @@ -11,7 +11,7 @@ "@wry/equality": "^0.1.9", "fast-json-stable-stringify": "^2.0.0", "graphql-tag": "^2.10.2", - "optimism": "^0.11.5", + "optimism": "^0.12.1", "symbol-observable": "^1.2.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0", @@ -744,9 +744,9 @@ "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" }, "@wry/context": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.0.tgz", - "integrity": "sha512-yW5XFrWbRvv2/f86+g0YAfko7671SQg7Epn8lYjux4MZmIvtPvK2ts+vG1QAPu2w6GuBioEJknRa7K2LFj2kQw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.2.tgz", + "integrity": "sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==", "requires": { "tslib": "^1.9.3" } @@ -4765,11 +4765,11 @@ } }, "optimism": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.5.tgz", - "integrity": "sha512-twCHmBb64DYzEZ8A3O+TLCuF/RmZPBhXPQYv4agoiALRLlW9SidMzd7lwUP9mL0jOZhzhnBmb8ajqA00ECo/7g==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.12.1.tgz", + "integrity": "sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ==", "requires": { - "@wry/context": "^0.5.0" + "@wry/context": "^0.5.2" } }, "optionator": { diff --git a/examples/bundling/tree-shaking/rollup-ac3-no-react/package-lock.json b/examples/bundling/tree-shaking/rollup-ac3-no-react/package-lock.json index cbb615a0ba3..2bcb85fad08 100644 --- a/examples/bundling/tree-shaking/rollup-ac3-no-react/package-lock.json +++ b/examples/bundling/tree-shaking/rollup-ac3-no-react/package-lock.json @@ -11,7 +11,7 @@ "@wry/equality": "^0.1.9", "fast-json-stable-stringify": "^2.0.0", "graphql-tag": "^2.10.2", - "optimism": "^0.11.5", + "optimism": "^0.12.1", "symbol-observable": "^1.2.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0", @@ -24,9 +24,9 @@ "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" }, "@wry/context": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.0.tgz", - "integrity": "sha512-yW5XFrWbRvv2/f86+g0YAfko7671SQg7Epn8lYjux4MZmIvtPvK2ts+vG1QAPu2w6GuBioEJknRa7K2LFj2kQw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.2.tgz", + "integrity": "sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==", "requires": { "tslib": "^1.9.3" } @@ -50,11 +50,11 @@ "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==" }, "optimism": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.5.tgz", - "integrity": "sha512-twCHmBb64DYzEZ8A3O+TLCuF/RmZPBhXPQYv4agoiALRLlW9SidMzd7lwUP9mL0jOZhzhnBmb8ajqA00ECo/7g==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.12.1.tgz", + "integrity": "sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ==", "requires": { - "@wry/context": "^0.5.0" + "@wry/context": "^0.5.2" } }, "symbol-observable": { diff --git a/examples/bundling/tree-shaking/rollup-ac3/package-lock.json b/examples/bundling/tree-shaking/rollup-ac3/package-lock.json index 7ba5b57222e..0a3a6bfc57d 100644 --- a/examples/bundling/tree-shaking/rollup-ac3/package-lock.json +++ b/examples/bundling/tree-shaking/rollup-ac3/package-lock.json @@ -11,7 +11,7 @@ "@wry/equality": "^0.1.9", "fast-json-stable-stringify": "^2.0.0", "graphql-tag": "^2.10.2", - "optimism": "^0.11.5", + "optimism": "^0.12.1", "symbol-observable": "^1.2.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0", @@ -24,9 +24,9 @@ "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" }, "@wry/context": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.0.tgz", - "integrity": "sha512-yW5XFrWbRvv2/f86+g0YAfko7671SQg7Epn8lYjux4MZmIvtPvK2ts+vG1QAPu2w6GuBioEJknRa7K2LFj2kQw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.2.tgz", + "integrity": "sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==", "requires": { "tslib": "^1.9.3" } @@ -50,11 +50,11 @@ "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==" }, "optimism": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.5.tgz", - "integrity": "sha512-twCHmBb64DYzEZ8A3O+TLCuF/RmZPBhXPQYv4agoiALRLlW9SidMzd7lwUP9mL0jOZhzhnBmb8ajqA00ECo/7g==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.12.1.tgz", + "integrity": "sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ==", "requires": { - "@wry/context": "^0.5.0" + "@wry/context": "^0.5.2" } }, "symbol-observable": { @@ -908,7 +908,8 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -969,14 +970,6 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -1062,11 +1055,6 @@ "path-key": "^2.0.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, "on-headers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", @@ -1176,16 +1164,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -1223,31 +1201,103 @@ } }, "react": { - "version": "16.13.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.0.tgz", - "integrity": "sha512-TSavZz2iSLkq5/oiE7gnFzmURKZMltmi193rm5HEoUDAXpzT9Kzw6oNZnGoai/4+fUnm7FqS5dwgUL34TujcWQ==", + "version": "file:../../../../node_modules/react", + "integrity": "sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==" + } } }, "react-dom": { - "version": "16.13.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz", - "integrity": "sha512-y09d2c4cG220DzdlFkPTnVvGTszVvNpC73v+AaLGLHbkpy3SSgvYq8x0rNwPJ/Rk/CicTNgk0hbHNw1gMEZAXg==", + "version": "file:../../../../node_modules/react-dom", + "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.19.0" + "scheduler": "^0.15.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==" + }, + "scheduler": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", + "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } }, - "react-is": { - "version": "16.13.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", - "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==" - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -1473,15 +1523,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "scheduler": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.0.tgz", - "integrity": "sha512-xowbVaTPe9r7y7RUejcK73/j8tt2jfiyTednOvHbA8JoClvMYCp+r8QegLwK/n8zWQAtZb1fFnER4XLBZXrCxA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, "serialize-javascript": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", diff --git a/examples/bundling/tree-shaking/rollup-ac3/package.json b/examples/bundling/tree-shaking/rollup-ac3/package.json index fce2fbe1cbf..8df3816e528 100644 --- a/examples/bundling/tree-shaking/rollup-ac3/package.json +++ b/examples/bundling/tree-shaking/rollup-ac3/package.json @@ -30,8 +30,8 @@ "dependencies": { "@apollo/client": "file:../../../../dist", "graphql": "^14.5.8", - "react": "^16.13.0", - "react-dom": "^16.13.0" + "react": "file:../../../../node_modules/react", + "react-dom": "file:../../../../node_modules/react-dom" }, "devDependencies": { "@babel/preset-react": "^7.0.0", diff --git a/src/react/context/ApolloConsumer.tsx b/src/react/context/ApolloConsumer.tsx index 6014d6e42b0..8c5c1d7f1fe 100644 --- a/src/react/context/ApolloConsumer.tsx +++ b/src/react/context/ApolloConsumer.tsx @@ -1,15 +1,14 @@ +import React from 'react'; import { invariant } from 'ts-invariant'; import { ApolloClient } from '../../ApolloClient'; import { getApolloContext } from './ApolloContext'; -import { requireReactLazily } from '../react'; export interface ApolloConsumerProps { children: (client: ApolloClient) => React.ReactChild | null; } export const ApolloConsumer: React.FC = props => { - const React = requireReactLazily(); const ApolloContext = getApolloContext(); return ( diff --git a/src/react/context/ApolloContext.ts b/src/react/context/ApolloContext.ts index 230881975e8..e9fa0911796 100644 --- a/src/react/context/ApolloContext.ts +++ b/src/react/context/ApolloContext.ts @@ -1,5 +1,5 @@ +import React from 'react'; import { ApolloClient } from '../../ApolloClient'; -import { requireReactLazily } from '../react'; export interface ApolloContextValue { client?: ApolloClient; @@ -20,7 +20,6 @@ const contextSymbol = typeof Symbol === 'function' && Symbol.for ? '__APOLLO_CONTEXT__'; export function resetApolloContext() { - const React = requireReactLazily(); Object.defineProperty(React, contextSymbol, { value: React.createContext({}), enumerable: false, @@ -30,7 +29,6 @@ export function resetApolloContext() { } export function getApolloContext() { - const React = requireReactLazily(); if (!(React as any)[contextSymbol]) { resetApolloContext(); } diff --git a/src/react/context/ApolloProvider.tsx b/src/react/context/ApolloProvider.tsx index 1667c451072..42fa65d1b2f 100644 --- a/src/react/context/ApolloProvider.tsx +++ b/src/react/context/ApolloProvider.tsx @@ -1,8 +1,8 @@ +import React from 'react'; import { invariant } from 'ts-invariant'; import { ApolloClient } from '../../ApolloClient'; import { getApolloContext } from './ApolloContext'; -import { requireReactLazily } from '../react'; export interface ApolloProviderProps { client: ApolloClient; @@ -13,7 +13,6 @@ export const ApolloProvider: React.FC> = ({ client, children }) => { - const React = requireReactLazily(); const ApolloContext = getApolloContext(); return ( diff --git a/src/react/context/__tests__/ApolloConsumer.test.tsx b/src/react/context/__tests__/ApolloConsumer.test.tsx index 811f4dd349a..27d3d96320b 100644 --- a/src/react/context/__tests__/ApolloConsumer.test.tsx +++ b/src/react/context/__tests__/ApolloConsumer.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { render, cleanup } from '@testing-library/react'; import { ApolloLink } from '../../../link/core/ApolloLink'; @@ -6,9 +7,6 @@ import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; import { ApolloProvider } from '../ApolloProvider'; import { ApolloConsumer } from '../ApolloConsumer'; import { getApolloContext } from '../ApolloContext'; -import { requireReactLazily } from '../../react'; - -const React = requireReactLazily(); const client = new ApolloClient({ cache: new Cache(), diff --git a/src/react/context/__tests__/ApolloProvider.test.tsx b/src/react/context/__tests__/ApolloProvider.test.tsx index d2c18befb85..9ef9d72ba09 100644 --- a/src/react/context/__tests__/ApolloProvider.test.tsx +++ b/src/react/context/__tests__/ApolloProvider.test.tsx @@ -1,3 +1,4 @@ +import React, { useContext } from 'react'; import { render, cleanup } from '@testing-library/react'; import { ApolloLink } from '../../../link/core/ApolloLink'; @@ -5,10 +6,6 @@ import { ApolloClient } from '../../../ApolloClient'; import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; import { ApolloProvider } from '../ApolloProvider'; import { getApolloContext } from '../ApolloContext'; -import { requireReactLazily } from '../../react'; - -const React = requireReactLazily(); -const { useContext } = React; describe(' Component', () => { afterEach(cleanup); diff --git a/src/react/hooks/__tests__/useApolloClient.test.tsx b/src/react/hooks/__tests__/useApolloClient.test.tsx index 54b08714478..1a1490efbb7 100644 --- a/src/react/hooks/__tests__/useApolloClient.test.tsx +++ b/src/react/hooks/__tests__/useApolloClient.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { render, cleanup } from '@testing-library/react'; import { InvariantError } from 'ts-invariant'; @@ -7,9 +8,6 @@ import { ApolloClient } from '../../../ApolloClient'; import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; import { useApolloClient } from '../useApolloClient'; import { resetApolloContext } from '../../context/ApolloContext'; -import { requireReactLazily } from '../../react'; - -const React = requireReactLazily(); describe('useApolloClient Hook', () => { afterEach(() => { diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx index a3fc9beb3f1..4f26803c37e 100644 --- a/src/react/hooks/__tests__/useLazyQuery.test.tsx +++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { DocumentNode } from 'graphql'; import gql from 'graphql-tag'; import { render, wait } from '@testing-library/react'; @@ -7,9 +8,6 @@ import { ApolloClient } from '../../../ApolloClient'; import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; import { ApolloProvider } from '../../context/ApolloProvider'; import { useLazyQuery } from '../useLazyQuery'; -import { requireReactLazily } from '../../react'; - -const React = requireReactLazily(); describe('useLazyQuery Hook', () => { const CAR_QUERY: DocumentNode = gql` diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx index 45d935de725..4afa79add91 100644 --- a/src/react/hooks/__tests__/useMutation.test.tsx +++ b/src/react/hooks/__tests__/useMutation.test.tsx @@ -1,3 +1,4 @@ +import React, { useEffect } from 'react'; import { DocumentNode, GraphQLError } from 'graphql'; import gql from 'graphql-tag'; import { render, cleanup, wait } from '@testing-library/react'; @@ -8,10 +9,6 @@ import { ApolloClient } from '../../../ApolloClient'; import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; import { ApolloProvider } from '../../context/ApolloProvider'; import { useMutation } from '../useMutation'; -import { requireReactLazily } from '../../react'; - -const React = requireReactLazily(); -const { useEffect } = React; describe('useMutation Hook', () => { interface Todo { diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index 9ba5c3b44c5..db21a681c01 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -1,3 +1,4 @@ +import React, { useState, useReducer, Fragment } from 'react'; import { DocumentNode, GraphQLError } from 'graphql'; import gql from 'graphql-tag'; import { render, cleanup, wait } from '@testing-library/react'; @@ -11,13 +12,9 @@ import { ApolloClient } from '../../../ApolloClient'; import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; import { ApolloProvider } from '../../context/ApolloProvider'; import { useQuery } from '../useQuery'; -import { requireReactLazily } from '../../react'; import { QueryFunctionOptions } from '../..'; import { NetworkStatus } from '../../../core/networkStatus'; -const React = requireReactLazily(); -const { useState, useReducer, Fragment } = React; - describe('useQuery Hook', () => { const CAR_QUERY: DocumentNode = gql` query { diff --git a/src/react/hooks/__tests__/useSubscription.test.tsx b/src/react/hooks/__tests__/useSubscription.test.tsx index 9211b411aba..c5d66c826a0 100644 --- a/src/react/hooks/__tests__/useSubscription.test.tsx +++ b/src/react/hooks/__tests__/useSubscription.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { render, cleanup, wait } from '@testing-library/react'; import gql from 'graphql-tag'; @@ -6,9 +7,6 @@ import { ApolloClient } from '../../../ApolloClient'; import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; import { ApolloProvider } from '../../context/ApolloProvider'; import { useSubscription } from '../useSubscription'; -import { requireReactLazily } from '../../react'; - -const React = requireReactLazily(); describe('useSubscription Hook', () => { afterEach(cleanup); diff --git a/src/react/hooks/useApolloClient.ts b/src/react/hooks/useApolloClient.ts index 7507ae65adc..c7b4deb4d3d 100644 --- a/src/react/hooks/useApolloClient.ts +++ b/src/react/hooks/useApolloClient.ts @@ -1,11 +1,10 @@ +import React from 'react'; import { invariant } from 'ts-invariant'; import { ApolloClient } from '../../ApolloClient'; import { getApolloContext } from '../context/ApolloContext'; -import { requireReactLazily } from '../react'; export function useApolloClient(): ApolloClient { - const React = requireReactLazily(); const { client } = React.useContext(getApolloContext()); invariant( client, diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts index 4b2ef906742..902ebe85101 100644 --- a/src/react/hooks/useMutation.ts +++ b/src/react/hooks/useMutation.ts @@ -1,16 +1,15 @@ +import { useContext, useState, useRef, useEffect } from 'react'; import { DocumentNode } from 'graphql'; import { MutationHookOptions, MutationTuple } from '../types/types'; import { MutationData } from '../data/MutationData'; import { OperationVariables } from '../../core/types'; import { getApolloContext } from '../context/ApolloContext'; -import { requireReactLazily } from '../react'; export function useMutation( mutation: DocumentNode, options?: MutationHookOptions ): MutationTuple { - const { useContext, useState, useRef, useEffect } = requireReactLazily(); const context = useContext(getApolloContext()); const [result, setResult] = useState({ called: false, loading: false }); const updatedOptions = options ? { ...options, mutation } : { mutation }; diff --git a/src/react/hooks/useSubscription.ts b/src/react/hooks/useSubscription.ts index a583554925a..a5dad16bc61 100644 --- a/src/react/hooks/useSubscription.ts +++ b/src/react/hooks/useSubscription.ts @@ -1,17 +1,15 @@ +import { useContext, useState, useRef, useEffect } from 'react'; import { DocumentNode } from 'graphql'; import { SubscriptionHookOptions } from '../types/types'; import { SubscriptionData } from '../data/SubscriptionData'; import { OperationVariables } from '../../core/types'; import { getApolloContext } from '../context/ApolloContext'; -import { requireReactLazily } from '../react'; export function useSubscription( subscription: DocumentNode, options?: SubscriptionHookOptions ) { - const React = requireReactLazily(); - const { useContext, useState, useRef, useEffect } = React; const context = useContext(getApolloContext()); const updatedOptions = options ? { ...options, subscription } diff --git a/src/react/hooks/utils/useBaseQuery.ts b/src/react/hooks/utils/useBaseQuery.ts index ff315a5830d..18cc63bd324 100644 --- a/src/react/hooks/utils/useBaseQuery.ts +++ b/src/react/hooks/utils/useBaseQuery.ts @@ -1,3 +1,4 @@ +import { useContext, useEffect, useReducer, useRef } from 'react'; import { DocumentNode } from 'graphql'; import { @@ -10,15 +11,12 @@ import { QueryData } from '../../data/QueryData'; import { useDeepMemo } from './useDeepMemo'; import { OperationVariables } from '../../../core/types'; import { getApolloContext } from '../../context/ApolloContext'; -import { requireReactLazily } from '../../react'; export function useBaseQuery( query: DocumentNode, options?: QueryHookOptions, lazy = false ) { - const React = requireReactLazily(); - const { useContext, useEffect, useReducer, useRef } = React; const context = useContext(getApolloContext()); const [tick, forceUpdate] = useReducer(x => x + 1, 0); const updatedOptions = options ? { ...options, query } : { query }; diff --git a/src/react/hooks/utils/useDeepMemo.ts b/src/react/hooks/utils/useDeepMemo.ts index a1f4882952a..868804e1bd4 100644 --- a/src/react/hooks/utils/useDeepMemo.ts +++ b/src/react/hooks/utils/useDeepMemo.ts @@ -1,7 +1,6 @@ +import { useRef } from 'react'; import { equal } from '@wry/equality'; -import { requireReactLazily } from '../../react'; - /** * Memoize a result using deep equality. This hook has two advantages over * React.useMemo: it uses deep equality to compare memo keys, and it guarantees @@ -13,8 +12,6 @@ export function useDeepMemo( memoFn: () => TValue, key: TKey ): TValue { - const React = requireReactLazily(); - const { useRef } = React; const ref = useRef<{ key: TKey; value: TValue }>(); if (!ref.current || !equal(key, ref.current.key)) { diff --git a/src/react/react.ts b/src/react/react.ts deleted file mode 100644 index 872f9f2ddf7..00000000000 --- a/src/react/react.ts +++ /dev/null @@ -1,9 +0,0 @@ -let React: typeof import('react'); - -// Apollo Client can be used without React, which means we want to make sure -// `react` is only imported/required if actually needed. To help with this -// the `react` module is lazy loaded using `requireReactLazily` when used by -// Apollo Client's React integration layer. -export function requireReactLazily(): typeof import('react') { - return React || (React = require('react')); -} From 0b8845b048630fbd8d16a7a28fd6d85582ea9ded Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 29 May 2020 16:51:43 -0400 Subject: [PATCH 2/2] Use String.prototype.split for CJS React munging. --- config/rollup.config.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/config/rollup.config.js b/config/rollup.config.js index 429b891d76c..264a9acbd19 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -76,18 +76,20 @@ function prepareCJS(input, output) { const cjsBundle = output.replace(`${distDir}/`, ''); return { generateBundle(_option, bundle) { - const { code } = bundle[cjsBundle]; - const regex = /var React = require\('react'\);/; - const matches = code.match(regex); - if (matches && matches.length === 1) { - bundle[cjsBundle].code = - code.replace( - regex, - "try { var React = require('react'); } catch (error) {}" - ); + const parts = bundle[cjsBundle].code.split( + /var React = require\('react'\);/); + // The React import should appear only once in the CJS bundle, + // since we build the CJS bundle using Rollup, which (hopefully!) + // deduplicates all external imports. + if (parts && parts.length === 2) { + bundle[cjsBundle].code = [ + parts[0], + "try { var React = require('react'); } catch (error) {}", + parts[1], + ].join("\n"); } else { throw new Error( - 'The CJS bundle could not prepared as a single React ' + + 'The CJS bundle could not be prepared as a single React ' + 'require could not be found.' ); }