From 623fab5b8df672d15f98c090e1cf7b9c0978cd35 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Mon, 23 Aug 2021 12:44:51 -0400 Subject: [PATCH 1/4] Fix #8674 by polyfilling __DEV__ upon @apollo/client/link/http import. --- src/link/http/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/link/http/index.ts b/src/link/http/index.ts index d3e2a47166a..be401fb3637 100644 --- a/src/link/http/index.ts +++ b/src/link/http/index.ts @@ -1,3 +1,8 @@ +import { invariant } from "ts-invariant"; +import { DEV } from "../../utilities"; +// Since createHttpLink uses __DEV__, we must be sure to polyfill it. +invariant("boolean" === typeof DEV, DEV); + export { parseAndCheckHttpResponse, ServerParseError From b435c0b34eb58eae1c2220d83421f3529bf98388 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Mon, 23 Aug 2021 14:22:18 -0400 Subject: [PATCH 2/4] Make every @apollo/client/* entry point using __DEV__ call checkDEV(). --- src/__tests__/__snapshots__/exports.ts.snap | 1 + src/cache/index.ts | 5 ++--- src/core/index.ts | 3 ++- src/errors/index.ts | 5 ++--- src/link/core/index.ts | 5 ++--- src/link/http/index.ts | 6 ++---- src/link/persisted-queries/index.ts | 3 +++ src/link/utils/index.ts | 3 +++ src/react/context/index.ts | 3 +++ src/react/data/index.ts | 3 +++ src/react/hoc/index.ts | 3 +++ src/react/hooks/index.ts | 3 +++ src/react/index.ts | 5 ++--- src/react/parser/index.ts | 3 +++ src/testing/index.ts | 6 +++--- src/utilities/globals/index.ts | 5 +++++ src/utilities/index.ts | 7 +++---- 17 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/__tests__/__snapshots__/exports.ts.snap b/src/__tests__/__snapshots__/exports.ts.snap index 3835a31be88..c4d08899bb5 100644 --- a/src/__tests__/__snapshots__/exports.ts.snap +++ b/src/__tests__/__snapshots__/exports.ts.snap @@ -331,6 +331,7 @@ Array [ "buildQueryFromSelectionSet", "canUseWeakMap", "canUseWeakSet", + "checkDEV", "checkDocument", "cloneDeep", "compact", diff --git a/src/cache/index.ts b/src/cache/index.ts index 0aa33bdd2c8..f88a9510206 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -1,6 +1,5 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "../utilities"; -invariant("boolean" === typeof DEV, DEV); +import { checkDEV } from "../utilities"; +checkDEV(); export { Transaction, ApolloCache } from './core/cache'; export { Cache } from './core/types/Cache'; diff --git a/src/core/index.ts b/src/core/index.ts index b3723e5af78..13c8744f273 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,6 +1,7 @@ /* Core */ -import { DEV } from "../utilities"; +import { DEV, checkDEV } from "../utilities"; +checkDEV(); export { ApolloClient, diff --git a/src/errors/index.ts b/src/errors/index.ts index e053540cbed..03542d7b40c 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -1,6 +1,5 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "../utilities"; -invariant("boolean" === typeof DEV, DEV); +import { checkDEV } from "../utilities"; +checkDEV(); import { GraphQLError } from 'graphql'; diff --git a/src/link/core/index.ts b/src/link/core/index.ts index b01ccf98897..90047fbb6e0 100644 --- a/src/link/core/index.ts +++ b/src/link/core/index.ts @@ -1,6 +1,5 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "../../utilities"; -invariant("boolean" === typeof DEV, DEV); +import { checkDEV } from "../../utilities"; +checkDEV(); export { empty } from './empty'; export { from } from './from'; diff --git a/src/link/http/index.ts b/src/link/http/index.ts index be401fb3637..d7110fa09e3 100644 --- a/src/link/http/index.ts +++ b/src/link/http/index.ts @@ -1,7 +1,5 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "../../utilities"; -// Since createHttpLink uses __DEV__, we must be sure to polyfill it. -invariant("boolean" === typeof DEV, DEV); +import { checkDEV } from "../../utilities"; +checkDEV(); export { parseAndCheckHttpResponse, diff --git a/src/link/persisted-queries/index.ts b/src/link/persisted-queries/index.ts index 31b9eb8582b..2e57399d945 100644 --- a/src/link/persisted-queries/index.ts +++ b/src/link/persisted-queries/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + import { print } from 'graphql'; import { DocumentNode, diff --git a/src/link/utils/index.ts b/src/link/utils/index.ts index 5c28f516365..e06e5a3abe3 100644 --- a/src/link/utils/index.ts +++ b/src/link/utils/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + export { fromError } from './fromError'; export { toPromise } from './toPromise'; export { fromPromise } from './fromPromise'; diff --git a/src/react/context/index.ts b/src/react/context/index.ts index 860b3839b46..64a3908792a 100644 --- a/src/react/context/index.ts +++ b/src/react/context/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + export * from './ApolloConsumer'; export * from './ApolloContext'; export * from './ApolloProvider'; diff --git a/src/react/data/index.ts b/src/react/data/index.ts index 26776e66350..9da77854854 100644 --- a/src/react/data/index.ts +++ b/src/react/data/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + export { SubscriptionData } from './SubscriptionData'; export { OperationData } from './OperationData'; export { MutationData } from './MutationData'; diff --git a/src/react/hoc/index.ts b/src/react/hoc/index.ts index bb34080ee45..00f41e66d05 100644 --- a/src/react/hoc/index.ts +++ b/src/react/hoc/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + export { graphql } from './graphql'; export { withQuery } from './query-hoc'; diff --git a/src/react/hooks/index.ts b/src/react/hooks/index.ts index a9a323f7fc1..6d876cfea4d 100644 --- a/src/react/hooks/index.ts +++ b/src/react/hooks/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + export * from './useApolloClient'; export * from './useLazyQuery'; export * from './useMutation'; diff --git a/src/react/index.ts b/src/react/index.ts index eeee3f721a2..deab19a3cd3 100644 --- a/src/react/index.ts +++ b/src/react/index.ts @@ -1,6 +1,5 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "../utilities"; -invariant("boolean" === typeof DEV, DEV); +import { checkDEV } from "../utilities"; +checkDEV(); export { ApolloProvider, diff --git a/src/react/parser/index.ts b/src/react/parser/index.ts index ad0cbf69371..6a43a8d5853 100644 --- a/src/react/parser/index.ts +++ b/src/react/parser/index.ts @@ -1,3 +1,6 @@ +import { checkDEV } from "../../utilities"; +checkDEV(); + import { DocumentNode, DefinitionNode, diff --git a/src/testing/index.ts b/src/testing/index.ts index 831c6de2b10..029df077cad 100644 --- a/src/testing/index.ts +++ b/src/testing/index.ts @@ -1,4 +1,4 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "../utilities"; -invariant("boolean" === typeof DEV, DEV); +import { checkDEV } from "../utilities"; +checkDEV(); + export * from '../utilities/testing'; diff --git a/src/utilities/globals/index.ts b/src/utilities/globals/index.ts index ae5e9ba808f..cb481e245b7 100644 --- a/src/utilities/globals/index.ts +++ b/src/utilities/globals/index.ts @@ -1,7 +1,12 @@ +import { invariant } from "ts-invariant"; + // Just in case the graphql package switches from process.env.NODE_ENV to // __DEV__, make sure __DEV__ is polyfilled before importing graphql. import DEV from "./DEV"; export { DEV } +export function checkDEV() { + invariant("boolean" === typeof DEV, DEV); +} // Import graphql/jsutils/instanceOf safely, working around its unchecked usage // of process.env.NODE_ENV and https://github.com/graphql/graphql-js/pull/2894. diff --git a/src/utilities/index.ts b/src/utilities/index.ts index 9f151b88386..7b5f3e3c61c 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -1,7 +1,6 @@ -import { invariant } from "ts-invariant"; -import { DEV } from "./globals"; -invariant("boolean" === typeof DEV, DEV); -export { DEV } +import { DEV, checkDEV } from "./globals"; +export { DEV, checkDEV } +checkDEV(); export { DirectiveInfo, From e1f01bf0b52d5aebdff54ae5d8e9a706a255232b Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Mon, 23 Aug 2021 13:54:54 -0400 Subject: [PATCH 3/4] Enforce checkDEV() called by every entry point that uses __DEV__. --- config/checkDEV.ts | 54 +++++++++++++++++++++++++++++++++++++++++++ config/entryPoints.js | 6 +++++ package.json | 3 ++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 config/checkDEV.ts diff --git a/config/checkDEV.ts b/config/checkDEV.ts new file mode 100644 index 00000000000..705f41ffa95 --- /dev/null +++ b/config/checkDEV.ts @@ -0,0 +1,54 @@ +import * as path from "path"; +import { readFileSync, promises as fs } from "fs"; +import { eachFile, distDir } from "./helpers"; + +const entryPoints = require("./entryPoints.js"); + +const filesWithDEV = new Set(); + +eachFile(distDir, async file => { + const source = await fs.readFile(file, "utf8"); + if (/\b__DEV__\b/.test(source)) { + filesWithDEV.add(file); + } +}).then(() => { + const filesByEntryPoint = new Map; + }>(); + + entryPoints.forEach(({ dirs }: { dirs: string[] }) => { + const relIndexPath = path.join(...dirs, "index.js"); + const absIndexPath = path.join(distDir, relIndexPath); + filesByEntryPoint.set(dirs.join("/"), { + indexPath: relIndexPath, + source: readFileSync(absIndexPath, "utf8"), + files: new Set(), + }); + }); + + filesWithDEV.forEach(file => { + const entryPointDir = entryPoints.getEntryPointDirectory(file); + const info = filesByEntryPoint.get(entryPointDir); + const absEntryPointDir = path.join(distDir, entryPointDir); + const relPath = "./" + path.relative(absEntryPointDir, file); + if (info) { + info.files.add(relPath); + } + }); + + filesByEntryPoint.forEach(({ indexPath, source, files }, entryPointDir) => { + if (!files.size || source.indexOf("checkDEV()") >= 0) { + return; + } + const entryPointId = `@apollo/client/${entryPointDir}`; + throw new Error(`Entry point ${ + entryPointId + }/index.js does not call checkDEV(), but ${ + entryPointId + } contains the following files that use __DEV__: ${ + Array.from(files).join(", ") + }`); + }); +}); diff --git a/config/entryPoints.js b/config/entryPoints.js index 71c05482311..809ecc251ef 100644 --- a/config/entryPoints.js +++ b/config/entryPoints.js @@ -92,6 +92,12 @@ function partsAfterDist(id) { } } +exports.getEntryPointDirectory = function (file) { + const parts = partsAfterDist(file) || file.split(path.sep); + const len = lengthOfLongestEntryPoint(parts); + if (len >= 0) return parts.slice(0, len).join(path.sep); +}; + function lengthOfLongestEntryPoint(parts) { let node = lookupTrie; let longest = -1; diff --git a/package.json b/package.json index b40150f5dd3..f04db73c7c5 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "scripts": { "prebuild": "npm run clean", "build": "tsc", - "postbuild": "npm run update-version && npm run invariants && npm run sourcemaps && npm run rollup && npm run prepdist && npm run resolve && npm run verify-version", + "postbuild": "npm run update-version && npm run invariants && npm run sourcemaps && npm run rollup && npm run prepdist && npm run resolve && npm run check-DEV && npm run verify-version", "update-version": "node config/version.js update", "verify-version": "node config/version.js verify", "invariants": "ts-node-script config/processInvariants.ts", @@ -39,6 +39,7 @@ "rollup": "rollup -c ./config/rollup.config.js", "prepdist": "node ./config/prepareDist.js", "resolve": "ts-node-script config/resolveModuleIds.ts", + "check-DEV": "ts-node-script config/checkDEV.ts", "clean": "rimraf -r dist coverage lib", "test": "jest --config ./config/jest.config.js", "test:debug": "BABEL_ENV=server node --inspect-brk node_modules/.bin/jest --config ./config/jest.config.js --runInBand", From 99fa6a0d4cf59cd5a2ee9a87d8b83aa23d7ae5b4 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Mon, 23 Aug 2021 14:42:13 -0400 Subject: [PATCH 4/4] Mention PR #8689 in CHANGELOG.md. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0ee8c0fcd..cce5b24fee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - Fix unhandled `Promise` rejection warnings/errors whose message is `Observable cancelled prematurely`.
[@benjamn](https://github.com/benjamn) in [#8676](https://github.com/apollographql/apollo-client/pull/8676) +- Enforce that `__DEV__` is polyfilled by every `@apollo/client/*` entry point that uses it. This build step considers not only explicit `__DEV__` usage but also `__DEV__` references injected near `invariant(...)` and `new InvariantError(...)` expressions.
+ [@benjamn](https://github.com/benjamn) in [#8689](https://github.com/apollographql/apollo-client/pull/8689) + ## Apollo Client 3.4.8 ### Bug Fixes