Skip to content

Commit b1bd747

Browse files
authored
Move utility functions out of apollo-env, leaving polyfills. (#2274)
* Move utility functions out of `apollo-env`, leaving polyfills. The `apollo-env` package has a number of purposes today, including providing polyfills for runtimes that lack more modern features, providing utility functions (like, `createHash` that works on non-Node.js environments) and offering some common predicate functions like `isNotNullOrUndefined` - a method that we use in many places in the Apollo ecosystem. The polyfills have proven useful on environments that necessitate them, offering newer ECMAScript functionality in runtimes that don't yet have them, but they're automatically loaded from the main module entry point of this library, rather than being selectively enabled. For example, it's not uncommon to see `import 'apollo-env';` in other packages that rely on the functionality. It's this automatic eager loading of polyfills that proves to be problematic when the _utilities_ and _predicate_ functions of this library come into play though. Imagine a package that merely needs to use a version of `createHash` that simply falls back gracefully to a JavaScript version, or a function that wants to utilize the useful `mapValues` function, but in a circumstance where we - or our users - don't want to be subjected to the nature of polyfills affecting the runtime (see related issues like apollographql/apollo-server#2634). Another circumstance that arises, which was the most motivating factor in this case, is that the `apollo-env` package has consistently proven difficult for bundlers to traverse, possibly because of the polyfills, possibly because of the side-effects, possibly because of both! However, in my times trying to bundle our Apollo packages over the years, I've encountered problems with Webpack, Snowpack, Rollup.js and now esbuild. Most of these functions are small enough and simple enough that the duplication cost isn't all that high. For example, in my changes in this commit, rather than paying the cost of relying on an other package for what is basically a one-line function, I've just copied and pasted `isNotNullOrUndefined` into a couple places where it is needed. Furthermore, since most of the utilities that were being leveraged from `apollo-env` were being leveraged _by_ the `apollo-graphql` package, I've just moved those into that package instead. Any runtimes that still necessitate polyfills like `Array.prototype.flat` or `Array.prototype.flatMap` can still `import "apollo-env";` directly for those side-effects to be applied to their runtme, and packages that just need the utilities can rely on `apollo-graphql`. Again though, most of our packages are depending on both already so creating the more distinct separation seems not-all-that-unreasonable. Furthermore, while `apollo-env` does also provide some `fetch` polyfills, once Node.js 12 becomes the minimum version we support, I don't think we even need those polyfills anymore at all. * Cease using ES features which don't exist natively on our target platforms Honestly, this whole attempt to use modern ECMAScript features before they were ready and truly supported in the runtimes we support was I think a great experiment, but not worth the struggle. We still support Node.js 10 for a couple more weeks, but this is a constraint for me today and it's (being, generally the `apollo-server-env` and the `apollo-env` package's polyfills) have been a constraint _many_ times over. Put another way, I don't think the Pandora's box of "polyfills" is worth it for things like `Array.protytype.flat`, which are not so inherently complex that they can't be abstracted into a small function. The `core-js-pure` package does a pretty good job offering this functionality without necessitating us in-house/vendor it ourselves, but it - sadly - does not provide TypeScript types, unlike the `core-js` core itself. * Update packages/apollo-graphql/src/schema/buildSchemaFromSDL.ts
1 parent 61974b9 commit b1bd747

15 files changed

Lines changed: 52 additions & 25 deletions

File tree

package-lock.json

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@
5555
"@fancy-test/nock": "0.1.1",
5656
"@oclif/dev-cli": "1.26.0",
5757
"@oclif/test": "1.2.8",
58-
"@types/babel__generator": "7.6.2",
5958
"@types/babel-types": "7.0.9",
59+
"@types/babel__generator": "7.6.2",
6060
"@types/common-tags": "1.8.0",
61+
"@types/core-js": "^2.5.4",
6162
"@types/cosmiconfig": "5.0.3",
6263
"@types/glob": "7.1.1",
6364
"@types/inflected": "1.1.29",

packages/apollo-env/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ import "./polyfills";
22

33
export * from "./typescript-utility-types";
44
export * from "./fetch";
5-
export * from "./utils";

packages/apollo-env/src/utils/index.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

packages/apollo-env/src/utils/predicates.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/apollo-graphql/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"node": ">=6"
1212
},
1313
"dependencies": {
14-
"apollo-env": "file:../apollo-env",
14+
"core-js-pure": "^3.10.2",
1515
"lodash.sortby": "^4.7.0"
1616
},
1717
"peerDependencies": {

packages/apollo-graphql/src/operationId.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
// defaultOperationRegistrySignature, which are slightly different normalization
5454
// functions used in other contextes.
5555
import { DocumentNode } from "graphql";
56-
import { createHash } from "apollo-env";
5756
import {
5857
printWithReducedWhitespace,
5958
dropUnusedDefinitions,
@@ -62,6 +61,7 @@ import {
6261
removeAliases,
6362
hideLiterals
6463
} from "./transforms";
64+
import { createHash } from "./utilities/createHash";
6565

6666
// The usage reporting signature function consists of removing extra whitespace,
6767
// sorting the AST in a deterministic manner, hiding literals, and removing

packages/apollo-graphql/src/schema/buildSchemaFromSDL.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ import { isDocumentNode, isNode } from "../utilities/graphql";
2525
import { GraphQLResolverMap } from "./resolverMap";
2626
import { GraphQLSchemaValidationError } from "./GraphQLSchemaValidationError";
2727
import { specifiedSDLRules } from "graphql/validation/specifiedRules";
28+
29+
// TODO(Node.js 10): When we deprecate Node.js 10, remove this and switch
30+
// to using `Array.prototype.flat`. When doing this, deleting the hand-rolled
31+
// types in `./packages/apollo-gateway/src/types/` that go with it.
32+
import flat from "core-js-pure/features/array/flat";
33+
2834
import {
2935
KnownTypeNamesRule,
3036
UniqueDirectivesPerLocationRule,
3137
ValidationRule
3238
} from "graphql/validation";
33-
import { mapValues, isNotNullOrUndefined } from "apollo-env";
39+
import { mapValues } from "../utilities/mapValues";
3440

3541
export interface GraphQLSchemaModule {
3642
typeDefs: DocumentNode;
@@ -156,7 +162,7 @@ export function buildSchemaFromSDL(
156162
{
157163
kind: Kind.DOCUMENT,
158164
definitions: [
159-
...Object.values(definitionsMap).flat(),
165+
...flat(Object.values(definitionsMap)),
160166
...missingTypeDefinitions,
161167
...directiveDefinitions
162168
]
@@ -170,7 +176,7 @@ export function buildSchemaFromSDL(
170176
schema,
171177
{
172178
kind: Kind.DOCUMENT,
173-
definitions: Object.values(extensionsMap).flat()
179+
definitions: flat(Object.values(extensionsMap))
174180
},
175181
{
176182
assumeValidSDL: true
@@ -182,10 +188,11 @@ export function buildSchemaFromSDL(
182188
if (schemaDefinitions.length > 0 || schemaExtensions.length > 0) {
183189
operationTypeMap = {};
184190

185-
const operationTypes = [...schemaDefinitions, ...schemaExtensions]
186-
.map(node => node.operationTypes)
187-
.filter(isNotNullOrUndefined)
188-
.flat();
191+
const operationTypes = flat(
192+
[...schemaDefinitions, ...schemaExtensions]
193+
.map(node => node.operationTypes)
194+
.filter(isNotNullOrUndefined)
195+
);
189196

190197
for (const { operation, type } of operationTypes) {
191198
operationTypeMap[operation] = type.name.value;
@@ -295,3 +302,7 @@ export function addResolversToSchema(
295302
}
296303
}
297304
}
305+
306+
function isNotNullOrUndefined<T>(value: T | null | undefined): value is T {
307+
return value !== null && typeof value !== "undefined";
308+
}

packages/apollo-graphql/src/schema/transformSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
GraphQLInputObjectType,
2222
GraphQLInputFieldConfigMap
2323
} from "graphql";
24-
import { mapValues } from "apollo-env";
24+
import { mapValues } from "../utilities/mapValues";
2525

2626
type TypeTransformer = (
2727
type: GraphQLNamedType
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module "core-js-pure/features/array/flat" {
2+
function flat<A>(
3+
array: A[],
4+
...args: Parameters<typeof Array.prototype.flat>
5+
): A;
6+
export = flat;
7+
}

0 commit comments

Comments
 (0)