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
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",
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 d3e2a47166a..d7110fa09e3 100644
--- a/src/link/http/index.ts
+++ b/src/link/http/index.ts
@@ -1,3 +1,6 @@
+import { checkDEV } from "../../utilities";
+checkDEV();
+
export {
parseAndCheckHttpResponse,
ServerParseError
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,