diff --git a/src/cache/inmemory/readFromStore.ts b/src/cache/inmemory/readFromStore.ts index d0b343db628..57e73223df8 100644 --- a/src/cache/inmemory/readFromStore.ts +++ b/src/cache/inmemory/readFromStore.ts @@ -27,6 +27,7 @@ import { maybeDeepFreeze, mergeDeepArray, } from '../../utilities'; +import { isProduction } from '../../utilities/common/environment'; import { Cache } from '../core/types/Cache'; import { DiffQueryAgainstStoreOptions, @@ -288,7 +289,7 @@ export class StoreReader { // as a scalar value. However, that value should not contain any // Reference objects, and should be frozen in development, if it // happens to be an object that is mutable. - if (process.env.NODE_ENV !== 'production') { + if (!isProduction()) { assertSelectionSetForIdValue( context.store, selection, @@ -337,7 +338,7 @@ export class StoreReader { // defensive shallow copies than necessary. finalResult.result = mergeDeepArray(objectsToMerge); - if (process.env.NODE_ENV !== 'production') { + if (!isProduction()) { Object.freeze(finalResult.result); } @@ -414,7 +415,7 @@ export class StoreReader { }), i); } - if (process.env.NODE_ENV !== 'production') { + if (!isProduction()) { assertSelectionSetForIdValue(context.store, field, item); } @@ -423,7 +424,7 @@ export class StoreReader { return item; }); - if (process.env.NODE_ENV !== 'production') { + if (!isProduction()) { Object.freeze(array); } diff --git a/src/cache/inmemory/writeToStore.ts b/src/cache/inmemory/writeToStore.ts index fea1d64bf5c..a511680a145 100644 --- a/src/cache/inmemory/writeToStore.ts +++ b/src/cache/inmemory/writeToStore.ts @@ -21,6 +21,7 @@ import { hasDirectives, cloneDeep, } from '../../utilities'; +import { isProduction } from '../../utilities/common/environment'; import { NormalizedCache, ReadMergeModifyContext } from './types'; import { makeProcessedFieldsMerger, FieldValueToBeMerged, fieldNameFromStoreName } from './helpers'; @@ -259,7 +260,7 @@ export class StoreWriter { mergedFields = policies.applyMerges(entityRef, mergedFields, context); } - if (process.env.NODE_ENV !== "production") { + if (!isProduction()) { Object.keys(mergedFields).forEach(storeFieldName => { const fieldName = fieldNameFromStoreName(storeFieldName); // If a merge function was defined for this field, trust that it @@ -293,7 +294,7 @@ export class StoreWriter { // In development, we need to clone scalar values so that they can be // safely frozen with maybeDeepFreeze in readFromStore.ts. In production, // it's cheaper to store the scalar values directly in the cache. - return process.env.NODE_ENV === 'production' ? value : cloneDeep(value); + return isProduction() ? value : cloneDeep(value); } if (Array.isArray(value)) { diff --git a/src/core/ApolloClient.ts b/src/core/ApolloClient.ts index 6fd3ca69062..fe9b51a5880 100644 --- a/src/core/ApolloClient.ts +++ b/src/core/ApolloClient.ts @@ -4,6 +4,7 @@ import { invariant, InvariantError } from 'ts-invariant'; import { ApolloLink, FetchResult, GraphQLRequest, execute } from '../link/core'; import { ApolloCache, DataProxy } from '../cache'; import { Observable, compact } from '../utilities'; +import { isProduction } from '../utilities/common/environment'; import { version } from '../version'; import { HttpLink, UriFunction } from '../link/http'; @@ -171,7 +172,7 @@ export class ApolloClient implements DataProxy { // Attach the client instance to window to let us be found by chrome devtools, but only in // development mode const defaultConnectToDevTools = - process.env.NODE_ENV !== 'production' && + !isProduction() && typeof window !== 'undefined' && !(window as any).__APOLLO_CLIENT__; @@ -186,7 +187,7 @@ export class ApolloClient implements DataProxy { /** * Suggest installing the devtools for developers who don't have them */ - if (!hasSuggestedDevtools && process.env.NODE_ENV !== 'production') { + if (!hasSuggestedDevtools && !isProduction()) { hasSuggestedDevtools = true; if ( typeof window !== 'undefined' && diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts index e880d8c1277..80457be0da2 100644 --- a/src/core/ObservableQuery.ts +++ b/src/core/ObservableQuery.ts @@ -19,6 +19,7 @@ import { FetchMoreQueryOptions, SubscribeToMoreOptions, } from './watchQueryOptions'; +import { isProduction } from '../utilities/common/environment'; import { Reobserver } from './Reobserver'; import { QueryInfo } from './QueryInfo'; @@ -312,7 +313,7 @@ export class ObservableQuery< const { updateQuery } = fetchMoreOptions; if (updateQuery) { - if (process.env.NODE_ENV !== "production" && + if (!isProduction() && !warnedAboutUpdateQuery) { invariant.warn( `The updateQuery callback for fetchMore is deprecated, and will be removed diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts index 6966570b9d1..e18d1abd28f 100644 --- a/src/core/QueryManager.ts +++ b/src/core/QueryManager.ts @@ -20,6 +20,7 @@ import { Concast, ConcastSourcesIterable, } from '../utilities'; +import { isProduction } from '../utilities/common/environment'; import { ApolloError, isApolloError } from '../errors'; import { MutationStore } from './MutationStore'; import { @@ -961,7 +962,7 @@ export class QueryManager { ) => { const data = diff.result as TData; - if (process.env.NODE_ENV !== 'production' && + if (!isProduction() && isNonEmptyArray(diff.missing) && !equal(data, {})) { invariant.warn(`Missing cache result fields: ${ diff --git a/src/utilities/common/__tests__/environment.ts b/src/utilities/common/__tests__/environment.ts index f9fe25362db..f655f4749ed 100644 --- a/src/utilities/common/__tests__/environment.ts +++ b/src/utilities/common/__tests__/environment.ts @@ -1,4 +1,9 @@ -import { isEnv, isDevelopment, isTest } from '../environment'; +import { refreshEnv, isEnv, isDevelopment, isTest } from '../environment'; + +function setNodeEnv(env: string|undefined) { + process.env.NODE_ENV = env; + refreshEnv(); +} describe('environment', () => { let keepEnv: string | undefined; @@ -10,48 +15,50 @@ describe('environment', () => { afterEach(() => { // restore the NODE_ENV - process.env.NODE_ENV = keepEnv; + setNodeEnv(keepEnv); }); describe('isEnv', () => { it(`should match when there's a value`, () => { ['production', 'development', 'test'].forEach(env => { - process.env.NODE_ENV = env; + setNodeEnv(env); expect(isEnv(env)).toBe(true); }); }); - it(`should treat no proces.env.NODE_ENV as it'd be in development`, () => { + it(`should treat no process.env.NODE_ENV as it'd be in development`, () => { delete process.env.NODE_ENV; + refreshEnv(); expect(isEnv('development')).toBe(true); }); }); describe('isTest', () => { it('should return true if in test', () => { - process.env.NODE_ENV = 'test'; + setNodeEnv('test'); expect(isTest()).toBe(true); }); it('should return true if not in test', () => { - process.env.NODE_ENV = 'development'; + setNodeEnv('development'); expect(!isTest()).toBe(true); }); }); describe('isDevelopment', () => { it('should return true if in development', () => { - process.env.NODE_ENV = 'development'; + setNodeEnv('development'); expect(isDevelopment()).toBe(true); }); it('should return true if not in development and environment is defined', () => { - process.env.NODE_ENV = 'test'; + setNodeEnv('test'); expect(!isDevelopment()).toBe(true); }); it('should make development as the default environment', () => { delete process.env.NODE_ENV; + refreshEnv(); expect(isDevelopment()).toBe(true); }); }); diff --git a/src/utilities/common/environment.ts b/src/utilities/common/environment.ts index 21d61f5f122..70eef9a30c6 100644 --- a/src/utilities/common/environment.ts +++ b/src/utilities/common/environment.ts @@ -1,10 +1,22 @@ -export function getEnv(): string | undefined { +// Functions for checking whether we're in a development, production, or test +// environment. Uses the NODE_ENV environment variable as the source of truth. +// This is cached on startup, because process.env is actually a C function and +// calling it is somewhat expensive (enough to show up prominently in profiler +// results, if not cached.) + +let node_env: string = "development"; + +export function refreshEnv(): void { if (typeof process !== 'undefined' && process.env.NODE_ENV) { - return process.env.NODE_ENV; + node_env = process.env.NODE_ENV; + } else { + node_env = "development"; } +} +refreshEnv(); - // default environment - return 'development'; +export function getEnv(): string | undefined { + return node_env; } export function isEnv(env: string): boolean { @@ -12,9 +24,13 @@ export function isEnv(env: string): boolean { } export function isDevelopment(): boolean { - return isEnv('development') === true; + return node_env==='development'; +} + +export function isProduction(): boolean { + return node_env==='production'; } export function isTest(): boolean { - return isEnv('test') === true; + return node_env==='test'; } diff --git a/src/utilities/common/maybeDeepFreeze.ts b/src/utilities/common/maybeDeepFreeze.ts index d35923589a1..c36307420f9 100644 --- a/src/utilities/common/maybeDeepFreeze.ts +++ b/src/utilities/common/maybeDeepFreeze.ts @@ -1,4 +1,4 @@ -import { isDevelopment, isTest } from './environment'; +import { isDevelopment, isProduction, isTest } from './environment'; function isObject(value: any) { return value !== null && typeof value === "object"; @@ -18,7 +18,7 @@ function deepFreeze(value: any) { } export function maybeDeepFreeze(obj: T): T { - if (process.env.NODE_ENV !== "production" && (isDevelopment() || isTest())) { + if (!isProduction() && (isDevelopment() || isTest())) { deepFreeze(obj); } return obj;