Skip to content

Commit 34a6c1d

Browse files
committed
Avoid importing concrete values when only types are needed
1 parent 7a9d78e commit 34a6c1d

112 files changed

Lines changed: 165 additions & 141 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/apollo-server/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import UserClass from "./models/User";
77
import queryAllUsersResolver from "./models/User";
88
import queryMeResolver from "./models/User";
9-
import { person as queryPersonResolver } from "./Query";
109
import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLString, GraphQLInterfaceType } from "graphql";
10+
import { person as queryPersonResolver } from "./Query";
1111
export function getSchema(): GraphQLSchema {
1212
const GroupType: GraphQLObjectType = new GraphQLObjectType({
1313
name: "Group",

examples/express-graphql-http/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import UserClass from "./models/User";
77
import queryAllUsersResolver from "./models/User";
88
import queryMeResolver from "./models/User";
9-
import { person as queryPersonResolver } from "./interfaces/IPerson";
109
import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLString, GraphQLInterfaceType } from "graphql";
10+
import { person as queryPersonResolver } from "./interfaces/IPerson";
1111
export function getSchema(): GraphQLSchema {
1212
const GroupType: GraphQLObjectType = new GraphQLObjectType({
1313
name: "Group",

examples/incremental-migration/schemas/gratsGeneratedSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// DO NOT USE DIRECTLY. Prefer the merged schema in `./mergedSchema.ts`.
22

3-
import { user as queryUserResolver } from "./../models";
43
import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLString, GraphQLID } from "graphql";
4+
import { user as queryUserResolver } from "./../models";
55
export function getSchema(): GraphQLSchema {
66
const UserType: GraphQLObjectType = new GraphQLObjectType({
77
name: "User",

examples/next-js/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
import UserClass from "./app/api/graphql/models/User";
77
import queryAllUsersResolver from "./app/api/graphql/models/User";
8-
import { me as queryMeResolver, person as queryPersonResolver } from "./app/api/graphql/Query";
98
import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLString, GraphQLInterfaceType } from "graphql";
9+
import { me as queryMeResolver, person as queryPersonResolver } from "./app/api/graphql/Query";
1010
export function getSchema(): GraphQLSchema {
1111
const GroupType: GraphQLObjectType = new GraphQLObjectType({
1212
name: "Group",

examples/production-app/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
* Do not manually edit. Regenerate by running `npx grats`.
44
*/
55

6-
import { GqlScalar } from "grats";
7-
import { GqlDate as DateInternal } from "./graphql/CustomScalars";
6+
import type { GqlScalar } from "grats";
7+
import type { GqlDate as DateInternal } from "./graphql/CustomScalars";
8+
import { GraphQLSchema, GraphQLDirective, DirectiveLocation, GraphQLNonNull, GraphQLInt, specifiedDirectives, GraphQLObjectType, GraphQLList, GraphQLString, GraphQLScalarType, GraphQLID, GraphQLInterfaceType, GraphQLBoolean, GraphQLInputObjectType } from "graphql";
89
import { id as likeIdResolver, id as userIdResolver, id as postIdResolver, node as queryNodeResolver, nodes as queryNodesResolver } from "./graphql/Node";
910
import { nodes as postConnectionNodesResolver, posts as queryPostsResolver } from "./models/PostConnection";
1011
import { nodes as likeConnectionNodesResolver, likes as queryLikesResolver, postLikes as subscriptionPostLikesResolver } from "./models/LikeConnection";
@@ -14,7 +15,6 @@ import { Viewer as queryViewerResolver } from "./models/Viewer";
1415
import { createLike as mutationCreateLikeResolver } from "./models/Like";
1516
import { createPost as mutationCreatePostResolver } from "./models/Post";
1617
import { createUser as mutationCreateUserResolver } from "./models/User";
17-
import { GraphQLSchema, GraphQLDirective, DirectiveLocation, GraphQLNonNull, GraphQLInt, specifiedDirectives, GraphQLObjectType, GraphQLList, GraphQLString, GraphQLScalarType, GraphQLID, GraphQLInterfaceType, GraphQLBoolean, GraphQLInputObjectType } from "graphql";
1818
export type SchemaConfig = {
1919
scalars: {
2020
Date: GqlScalar<DateInternal>;

examples/strict-semantic-nullability/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import UserClass from "./models/User";
77
import queryAllUsersResolver from "./models/User";
88
import queryMeResolver from "./models/User";
9-
import { defaultFieldResolver, GraphQLSchema, GraphQLDirective, DirectiveLocation, GraphQLList, GraphQLInt, specifiedDirectives, GraphQLObjectType, GraphQLNonNull, GraphQLString, GraphQLInterfaceType } from "graphql";
9+
import { GraphQLSchema, GraphQLDirective, DirectiveLocation, GraphQLList, GraphQLInt, specifiedDirectives, GraphQLObjectType, GraphQLNonNull, GraphQLString, defaultFieldResolver, GraphQLInterfaceType } from "graphql";
1010
import { person as queryPersonResolver } from "./interfaces/IPerson";
1111
import { countdown as subscriptionCountdownResolver, nullItems as subscriptionNullItemsResolver, nullIterable as subscriptionNullIterableResolver } from "./Subscription";
1212
async function assertNonNull<T>(value: T | Promise<T>): Promise<T> {

examples/yoga/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import UserClass from "./models/User";
77
import queryAllUsersResolver from "./models/User";
88
import queryMeResolver from "./models/User";
9+
import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLString, GraphQLInterfaceType, GraphQLInt } from "graphql";
910
import { person as queryPersonResolver } from "./interfaces/IPerson";
1011
import { countdown as subscriptionCountdownResolver } from "./Subscription";
11-
import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLString, GraphQLInterfaceType, GraphQLInt } from "graphql";
1212
export function getSchema(): GraphQLSchema {
1313
const GroupType: GraphQLObjectType = new GraphQLObjectType({
1414
name: "Group",

src/codegen/TSAstBuilder.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ export type JsonValue =
1515

1616
const F = ts.factory;
1717

18+
export type ImportSpecifier = {
19+
name: string;
20+
as?: string;
21+
isTypeOnly: boolean;
22+
};
23+
1824
/**
1925
* A helper class to build up a TypeScript document AST.
2026
*/
2127
export default class TSAstBuilder {
2228
_globalNames: Map<string, number> = new Map();
2329
_imports: ts.Statement[] = [];
24-
imports: Map<string, { name: string; as?: string }[]> = new Map();
30+
imports: Map<string, ImportSpecifier[]> = new Map();
2531
_helpers: ts.Statement[] = [];
2632
_statements: ts.Statement[] = [];
2733

@@ -170,21 +176,26 @@ export default class TSAstBuilder {
170176
);
171177
}
172178

173-
import(from: string, names: { name: string; as?: string }[]) {
179+
import(from: string, names: ImportSpecifier[]) {
174180
let moduleImports = this.imports.get(from);
175181
if (moduleImports == null) {
176182
moduleImports = [];
177183
this.imports.set(from, moduleImports);
178184
}
179-
for (const { name, as } of names) {
185+
for (const { name, as, isTypeOnly } of names) {
180186
let seen = false;
181187
for (const imp of moduleImports) {
182188
if (imp.name === name && imp.as === as) {
189+
// If a name is imported both as type only and as a value, it needs to
190+
// be imported as a value.
191+
if (imp.isTypeOnly && !isTypeOnly) {
192+
imp.isTypeOnly = false;
193+
}
183194
seen = true;
184195
}
185196
}
186197
if (!seen) {
187-
moduleImports.push({ name, as });
198+
moduleImports.push({ name, as, isTypeOnly });
188199
}
189200
}
190201
}
@@ -203,6 +214,7 @@ export default class TSAstBuilder {
203214
tsModulePath: string,
204215
exportName: string | null,
205216
localName: string,
217+
isTypeOnly: boolean,
206218
): void {
207219
const abs = resolveRelativePath(tsModulePath);
208220
const relative = replaceExt(
@@ -213,7 +225,9 @@ export default class TSAstBuilder {
213225
if (exportName == null) {
214226
this.importDefault(modulePath, localName);
215227
} else {
216-
this.import(modulePath, [{ name: exportName, as: localName }]);
228+
this.import(modulePath, [
229+
{ name: exportName, as: localName, isTypeOnly },
230+
]);
217231
}
218232
}
219233

@@ -228,16 +242,21 @@ export default class TSAstBuilder {
228242
);
229243

230244
for (const [from, names] of this.imports) {
245+
const allImportsAreTypeOnly = names.every((name) => name.isTypeOnly);
231246
const namedImports = names.map((name) => {
247+
// If all the imports are type only, then we don't need to mark each
248+
// individual import as type only.
249+
const isTypeOnly = !allImportsAreTypeOnly && name.isTypeOnly;
250+
232251
if (name.as && name.as !== name.name) {
233252
return F.createImportSpecifier(
234-
false,
253+
isTypeOnly,
235254
F.createIdentifier(name.name),
236255
F.createIdentifier(name.as),
237256
);
238257
} else {
239258
return F.createImportSpecifier(
240-
false,
259+
isTypeOnly,
241260
undefined,
242261
F.createIdentifier(name.name),
243262
);
@@ -247,7 +266,7 @@ export default class TSAstBuilder {
247266
F.createImportDeclaration(
248267
undefined,
249268
F.createImportClause(
250-
false,
269+
allImportsAreTypeOnly,
251270
undefined,
252271
F.createNamedImports(namedImports),
253272
),

src/codegen/resolverCodegen.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export default class ResolverCodegen {
7979
resolver.path,
8080
resolver.exportName,
8181
resolverName,
82+
false,
8283
);
8384
return this.ts.method(
8485
methodName,
@@ -109,6 +110,7 @@ export default class ResolverCodegen {
109110
resolver.path,
110111
resolver.exportName,
111112
resolverName,
113+
false,
112114
);
113115
return this.ts.method(
114116
methodName,
@@ -184,7 +186,7 @@ export default class ResolverCodegen {
184186
);
185187
case "derivedContext": {
186188
const localName = this.getDerivedContextName(arg.path, arg.exportName);
187-
this.ts.importUserConstruct(arg.path, arg.exportName, localName);
189+
this.ts.importUserConstruct(arg.path, arg.exportName, localName, false);
188190
return F.createCallExpression(
189191
F.createIdentifier(localName),
190192
undefined,
@@ -257,7 +259,9 @@ export default class ResolverCodegen {
257259
}
258260

259261
defaultResolverMethod(methodName: string): ts.MethodDeclaration {
260-
this.ts.import("graphql", [{ name: "defaultFieldResolver" }]);
262+
this.ts.import("graphql", [
263+
{ name: "defaultFieldResolver", isTypeOnly: false },
264+
]);
261265
return this.ts.method(
262266
methodName,
263267
RESOLVER_ARGS.map((name) => this.ts.param(name)),

src/codegen/resolverMapCodegen.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ class Codegen {
4949
// coupled to just TypeScript and GraphQL JS. Ideally we would not do
5050
// _anything_ coupled to other libraries but instead provide a way for users
5151
// to do this themselves.
52-
this.ts.import("@graphql-tools/utils", [{ name: "IResolvers" }]);
52+
this.ts.import("@graphql-tools/utils", [
53+
{ name: "IResolvers", isTypeOnly: true },
54+
]);
5355
this.ts.functionDeclaration(
5456
"getResolverMap",
5557
[F.createModifier(ts.SyntaxKind.ExportKeyword)],

0 commit comments

Comments
 (0)