From b3b37f747c00ba27a473ce2b30c160c9a7b82448 Mon Sep 17 00:00:00 2001 From: hwillson Date: Fri, 26 Mar 2021 06:42:55 -0400 Subject: [PATCH] Delay Apollo Client initialization in browser tabs until needed Apollo Client and its associated dependencies do not need to load on each browser tab, if the Apollo Client Devtools extension is not being used. These changes avoid loading / initializing AC and its deps, until they are really needed. This is ultimately a better fix for #457, which was being caused by Apollo Client's dependency `ts-invariant` being loaded when the devtools were initialized, even though they weren't being used on certain sites. --- CHANGELOG.md | 2 + package-lock.json | 133 ++++++++++++++++++++--------------- package.json | 6 +- src/extension/tab/helpers.ts | 49 +++++++++++-- src/extension/tab/hook.ts | 17 ++++- 5 files changed, 140 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6950b59c2..7a83adc97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Make sure null and boolean values are rendered properly in the Cache tree.
[@alexTayanovsky](https://github.com/alexTayanovsky) in [#446](https://github.com/apollographql/apollo-client-devtools/pull/446) +- Delay the loading / initialization of Apollo Client on each browser tab until it is really needed.
+ [@hwillson](https://github.com/apollographql/apollo-client-devtools) in [#479](https://github.com/apollographql/apollo-client-devtools/pull/479) ## 3.0.2 (2021-03-17) diff --git a/package-lock.json b/package-lock.json index 3394fc732..f8b76d244 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,23 +5,40 @@ "requires": true, "dependencies": { "@apollo/client": { - "version": "3.4.0-beta.15", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.4.0-beta.15.tgz", - "integrity": "sha512-xFi9q0PvC6lkQY/ympzQsj1BcptokVfd4MMfbd4XX1wbf1P1nztI8MpkIEiN5chIKtkO0NiS+bsW/DnRd+tQ1w==", + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.3.13.tgz", + "integrity": "sha512-usiVmXiOq0J/GpyIOIhlF8ItHpiPJObC7zoxLYPoOYj3G3OB0hCIcUKs3aTJ3ATW7u8QxvYgRaJg72NN7E1WOg==", "requires": { "@graphql-typed-document-node/core": "^3.0.0", - "@wry/context": "^0.5.2", - "@wry/equality": "^0.3.0", - "@wry/trie": "^0.2.1", + "@types/zen-observable": "^0.8.0", + "@wry/context": "^0.6.0", + "@wry/equality": "^0.4.0", "fast-json-stable-stringify": "^2.0.0", "graphql-tag": "^2.12.0", "hoist-non-react-statics": "^3.3.2", - "optimism": "^0.14.0", + "optimism": "^0.15.0", "prop-types": "^15.7.2", "symbol-observable": "^2.0.0", "ts-invariant": "^0.7.0", "tslib": "^1.10.0", - "zen-observable-ts": "^1.0.0" + "zen-observable": "^0.8.14" + }, + "dependencies": { + "graphql-tag": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.3.tgz", + "integrity": "sha512-5wJMjSvj30yzdciEuk9dPuUBUR56AqDi3xncoYQl1i42pGdSqOJrJsdb/rz5BDoy+qoGvQwABcBeF0xXY3TrKw==", + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + } } }, "@apollo/space-kit": { @@ -2497,11 +2514,6 @@ "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.2.tgz", "integrity": "sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==" }, - "@types/ungap__global-this": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@types/ungap__global-this/-/ungap__global-this-0.3.1.tgz", - "integrity": "sha512-+/DsiV4CxXl6ZWefwHZDXSe1Slitz21tom38qPCaG0DYCS1NnDPIQDTKcmQ/tvK/edJUKkmuIDBJbmKDiB0r/g==" - }, "@types/warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", @@ -2773,11 +2785,6 @@ "eslint-visitor-keys": "^1.1.0" } }, - "@ungap/global-this": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@ungap/global-this/-/global-this-0.4.4.tgz", - "integrity": "sha512-mHkm6FvepJECMNthFuIgpAEFmPOk71UyXuIxYfjytvFTnSDBIz7jmViO+LfHI/AjrazWije0PnSP3+/NlwzqtA==" - }, "@webassemblyjs/ast": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", @@ -2946,27 +2953,48 @@ "dev": true }, "@wry/context": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.4.tgz", - "integrity": "sha512-/pktJKHUXDr4D6TJqWgudOPJW2Z+Nb+bqk40jufA3uTkLbnCRKdJPiYDIa/c7mfcPH8Hr6O8zjCERpg5Sq04Zg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.6.0.tgz", + "integrity": "sha512-sAgendOXR8dM7stJw3FusRxFHF/ZinU0lffsA2YTyyIOfic86JX02qlPqPVqJNZJPAxFt+2EE8bvq6ZlS0Kf+Q==", "requires": { - "tslib": "^1.14.1" + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } } }, "@wry/equality": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.3.4.tgz", - "integrity": "sha512-1gQQhCPenzxw/1HzLlvSIs/59eBHJf9ZDIussjjZhqNSqQuPKQIzN6SWt4kemvlBPDi7RqMuUa03pId7MAE93g==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.4.0.tgz", + "integrity": "sha512-DxN/uawWfhRbgYE55zVCPOoe+jvsQ4m7PT1Wlxjyb/LCCLuU1UsucV2BbCxFAX8bjcSueFBbB5Qfj1Zfe8e7Fw==", "requires": { - "tslib": "^1.14.1" + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } } }, "@wry/trie": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.2.2.tgz", - "integrity": "sha512-OxqBB39x6MfHaa2HpMiRMfhuUnQTddD32Ko020eBeJXq87ivX6xnSSnzKHVbA21p7iqBASz8n/07b6W5wW1BVQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.3.0.tgz", + "integrity": "sha512-Yw1akIogPhAT6XPYsRHlZZIS0tIGmAl9EYXHi2scf7LPKKqdqmow/Hu4kEqP2cJR3EjaU/9L0ZlAjFf3hFxmug==", "requires": { - "tslib": "^1.14.1" + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } } }, "@xtuc/ieee754": { @@ -7019,12 +7047,9 @@ } }, "graphql-tag": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.1.tgz", - "integrity": "sha512-LPewEE1vzGkHnCO8zdOGogKsHHBdtpGyihow1UuMwp6RnZa0lAS7NcbvltLOuo4pi5diQCPASAXZkQq44ffixA==", - "requires": { - "tslib": "^1.14.1" - } + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.11.0.tgz", + "integrity": "sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA==" }, "growly": { "version": "1.3.0", @@ -10489,12 +10514,12 @@ } }, "optimism": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.14.1.tgz", - "integrity": "sha512-7+1lSN+LJEtaj3uBLLFk8uFCFKy3txLvcvln5Dh1szXjF9yghEMeWclmnk0qdtYZ+lcMNyu48RmQQRw+LRYKSQ==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.15.0.tgz", + "integrity": "sha512-KLKl3Kb7hH++s9ewRcBhmfpXgXF0xQ+JZ3xQFuPjnoT6ib2TDmYyVkKENmGxivsN2G3VRxpXuauCkB4GYOhtPw==", "requires": { - "@wry/context": "^0.5.2", - "@wry/trie": "^0.2.1" + "@wry/context": "^0.6.0", + "@wry/trie": "^0.3.0" } }, "optionator": { @@ -12995,13 +13020,18 @@ "dev": true }, "ts-invariant": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.7.0.tgz", - "integrity": "sha512-Ar5Y6ZSWZsN/e6A2WtbK8G0Z/+Qy6wsOOcucdoLQ2JZnbuorlEnXH003Ym6i4+X3C8rZNNmplYuingSQ8JSiWA==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.7.3.tgz", + "integrity": "sha512-UWDDeovyUTIMWj+45g5nhnl+8oo+GhxL5leTaHn5c8FkQWfh8v66gccLd2/YzVmV5hoQUjCEjhrXnQqVDJdvKA==", "requires": { - "@types/ungap__global-this": "^0.3.1", - "@ungap/global-this": "^0.4.2", - "tslib": "^1.9.3" + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } } }, "ts-is-defined": { @@ -14378,15 +14408,6 @@ "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" }, - "zen-observable-ts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.0.0.tgz", - "integrity": "sha512-KmWcbz+9kKUeAQ8btY8m1SsEFgBcp7h/Uf3V5quhan7ZWdjGsf0JcGLULQiwOZibbFWnHkYq8Nn2AZbJabovQg==", - "requires": { - "@types/zen-observable": "^0.8.2", - "zen-observable": "^0.8.15" - } - }, "zip-dir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/zip-dir/-/zip-dir-1.0.2.tgz", diff --git a/package.json b/package.json index 9f9111f55..bd94227be 100755 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "postinstall": "cd node_modules/graphiql-forked/packages/graphiql && npm install --production" }, "dependencies": { - "@apollo/client": "^3.4.0-beta.15", + "@apollo/client": "^3.3.13", "@apollo/space-kit": "^9.0.1-canary.317.8374.0", "@emotion/cache": "^11.1.3", "@emotion/react": "^11.1.5", @@ -37,12 +37,14 @@ "graphiql-forked": "https://github.com/apollographql/graphiql", "graphql": "^15.5.0", "graphql-syntax-highlighter-react": "^0.4.0", + "graphql-tag": "^2.11.0", "polished": "^4.1.1", "react": "^17.0.1", "react-copy-to-clipboard": "^5.0.3", "react-dom": "^17.0.1", "react-json-tree": "^0.15.0", - "stringify-object": "^3.3.0" + "stringify-object": "^3.3.0", + "zen-observable": "^0.8.15" }, "devDependencies": { "@emotion/jest": "11.2.1", diff --git a/src/extension/tab/helpers.ts b/src/extension/tab/helpers.ts index eef2555fc..cf67cff3d 100644 --- a/src/extension/tab/helpers.ts +++ b/src/extension/tab/helpers.ts @@ -1,4 +1,9 @@ -import { DocumentNode, Source } from "graphql"; +import { + DocumentNode, + Source, + OperationDefinitionNode, + FragmentDefinitionNode, +} from "graphql/language"; export type QueryInfo = { document: DocumentNode; @@ -11,13 +16,13 @@ export function getQueries(queryMap): QueryInfo[] { let queries: QueryInfo[] = []; if (queryMap) { - queries = [...queryMap.values()].map(({ - document, + queries = [...queryMap.values()].map(({ + document, variables, diff, }) => ({ document, - source: document?.loc?.source, + source: document?.loc?.source, variables, cachedData: diff?.result, }) @@ -39,7 +44,39 @@ export function getMutations(mutationsObj): QueryInfo[] { return { document: mutation, variables, - source: mutation?.loc?.source, + source: mutation?.loc?.source, } }); -} \ No newline at end of file +} + +export function getMainDefinition( + queryDoc: DocumentNode +): OperationDefinitionNode | FragmentDefinitionNode { + let fragmentDefinition; + + for (let definition of queryDoc.definitions) { + if (definition.kind === "OperationDefinition") { + const operation = (definition as OperationDefinitionNode).operation; + if ( + operation === "query" || + operation === "mutation" || + operation === "subscription" + ) { + return definition as OperationDefinitionNode; + } + } + if (definition.kind === "FragmentDefinition" && !fragmentDefinition) { + // we do this because we want to allow multiple fragment definitions + // to precede an operation definition. + fragmentDefinition = definition as FragmentDefinitionNode; + } + } + + if (fragmentDefinition) { + return fragmentDefinition; + } + + throw new Error( + "Expected a parsed GraphQL query with a query, mutation, subscription, or a fragment." + ); +} diff --git a/src/extension/tab/hook.ts b/src/extension/tab/hook.ts index 717b26617..84e97f0fd 100755 --- a/src/extension/tab/hook.ts +++ b/src/extension/tab/hook.ts @@ -1,9 +1,20 @@ -import { gql, Observable, ApolloClient } from "@apollo/client"; +import type { ApolloClient } from "@apollo/client"; + +// Note that we are intentionally not using Apollo Client's gql and +// Observable exports, as we don't want Apollo Client and its dependencies +// to be loaded into each browser tab, when this hook triggered. +import gql from "graphql-tag"; +import Observable from "zen-observable"; import { OperationDefinitionNode } from "graphql/language"; -import { getMainDefinition } from "@apollo/client/utilities"; + import { version as devtoolsVersion } from "../manifest.json"; import Relay from "../../Relay"; -import { QueryInfo, getQueries, getMutations } from "./helpers"; +import { + QueryInfo, + getQueries, + getMutations, + getMainDefinition, +} from "./helpers"; import { GraphiQLResponse, QueryResult } from '../../types'; import { CLIENT_FOUND,