Skip to content

Commit 30179ed

Browse files
committed
Refactor for library mode usage
1 parent c281c0a commit 30179ed

File tree

2 files changed

+240
-220
lines changed

2 files changed

+240
-220
lines changed

src/cli.ts

Lines changed: 6 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
11
import { readFile, writeFile } from "node:fs/promises";
22
import { parseArgs } from "node:util";
33

4-
import {
5-
buildSchema,
6-
GraphQLFieldConfig,
7-
GraphQLFieldConfigMap,
8-
GraphQLInterfaceType,
9-
GraphQLList,
10-
GraphQLNamedType,
11-
GraphQLNonNull,
12-
GraphQLObjectType,
13-
GraphQLOutputType,
14-
GraphQLSchema,
15-
GraphQLSemanticNonNull,
16-
GraphQLType,
17-
GraphQLUnionType,
18-
Kind,
19-
printSchema,
20-
validateSchema,
21-
} from "graphql";
22-
import type { Maybe } from "graphql/jsutils/Maybe";
4+
import { buildSchema, printSchema,validateSchema } from "graphql";
5+
6+
import { semanticToNullable,semanticToStrict } from "./index.js";
237

248
export async function main(toStrict = false) {
259
const {
@@ -51,203 +35,10 @@ export async function main(toStrict = false) {
5135
throw new Error("Invalid schema");
5236
}
5337

54-
const derivedSchema = convertSchema(schema, toStrict);
38+
const derivedSchema = toStrict
39+
? semanticToStrict(schema)
40+
: semanticToNullable(schema);
5541

5642
const newSdl = printSchema(derivedSchema);
5743
await writeFile(output, newSdl + "\n");
5844
}
59-
60-
function convertSchema(schema: GraphQLSchema, toStrict: boolean) {
61-
const config = schema.toConfig();
62-
const convertType = makeConvertType(toStrict);
63-
const derivedSchema = new GraphQLSchema({
64-
...config,
65-
query: convertType(config.query),
66-
mutation: convertType(config.mutation),
67-
subscription: convertType(config.subscription),
68-
types: config.types
69-
.filter((t) => !t.name.startsWith("__"))
70-
.map((t) => convertType(t)),
71-
directives: config.directives.filter((d) => d.name !== "semanticNonNull"),
72-
});
73-
return derivedSchema;
74-
}
75-
76-
export function semanticToNullable(schema: GraphQLSchema) {
77-
return convertSchema(schema, false);
78-
}
79-
80-
export function semanticToStrict(schema: GraphQLSchema) {
81-
return convertSchema(schema, true);
82-
}
83-
84-
function makeConvertType(toStrict: boolean) {
85-
const cache = new Map<string, GraphQLNamedType>();
86-
87-
function convertFields(fields: GraphQLFieldConfigMap<unknown, unknown>) {
88-
return () => {
89-
return Object.fromEntries(
90-
Object.entries(fields).map(([fieldName, inSpec]) => {
91-
const spec = applySemanticNonNullDirectiveToFieldConfig(inSpec);
92-
return [
93-
fieldName,
94-
{
95-
...spec,
96-
type: convertType(spec.type),
97-
},
98-
];
99-
}),
100-
) as GraphQLFieldConfigMap<unknown, unknown>;
101-
};
102-
}
103-
104-
function convertTypes(
105-
types: readonly GraphQLInterfaceType[] | null | undefined,
106-
): undefined | (() => readonly GraphQLInterfaceType[]);
107-
function convertTypes(
108-
types: readonly GraphQLObjectType[],
109-
): () => readonly GraphQLObjectType[];
110-
function convertTypes(
111-
types: readonly GraphQLNamedType[],
112-
): undefined | (() => readonly GraphQLNamedType[]);
113-
function convertTypes(
114-
types: readonly GraphQLNamedType[] | undefined,
115-
): undefined | (() => readonly GraphQLNamedType[]);
116-
function convertTypes(
117-
types: readonly GraphQLNamedType[] | null | undefined,
118-
): undefined | (() => readonly GraphQLNamedType[]) {
119-
if (!types) {
120-
return undefined;
121-
}
122-
return () => types.map((t) => convertType(t));
123-
}
124-
125-
function convertType(type: null | undefined): null | undefined;
126-
function convertType(type: GraphQLObjectType): GraphQLObjectType;
127-
function convertType(
128-
type: Maybe<GraphQLObjectType>,
129-
): Maybe<GraphQLObjectType>;
130-
function convertType(type: GraphQLNamedType): GraphQLNamedType;
131-
function convertType(type: GraphQLType): GraphQLType;
132-
function convertType(type: GraphQLType | null | undefined) {
133-
if (!type) {
134-
return type;
135-
}
136-
if (type instanceof GraphQLSemanticNonNull) {
137-
const unwrapped = convertType(type.ofType);
138-
// Here's where we do our thing!
139-
if (toStrict) {
140-
return new GraphQLNonNull(unwrapped);
141-
} else {
142-
return unwrapped;
143-
}
144-
} else if (type instanceof GraphQLNonNull) {
145-
return new GraphQLNonNull(convertType(type.ofType));
146-
} else if (type instanceof GraphQLList) {
147-
return new GraphQLList(convertType(type.ofType));
148-
}
149-
if (type.name.startsWith("__")) {
150-
return null;
151-
}
152-
if (cache.has(type.name)) {
153-
return cache.get(type.name);
154-
}
155-
const newType = (() => {
156-
if (type instanceof GraphQLObjectType) {
157-
const config = type.toConfig();
158-
return new GraphQLObjectType({
159-
...config,
160-
fields: convertFields(config.fields),
161-
interfaces: convertTypes(config.interfaces),
162-
});
163-
} else if (type instanceof GraphQLInterfaceType) {
164-
const config = type.toConfig();
165-
return new GraphQLInterfaceType({
166-
...config,
167-
fields: convertFields(config.fields),
168-
interfaces: convertTypes(config.interfaces),
169-
});
170-
} else if (type instanceof GraphQLUnionType) {
171-
const config = type.toConfig();
172-
return new GraphQLUnionType({
173-
...config,
174-
types: convertTypes(config.types),
175-
});
176-
} else {
177-
return type;
178-
}
179-
})();
180-
cache.set(type.name, newType);
181-
return newType;
182-
}
183-
184-
return convertType;
185-
}
186-
187-
/**
188-
* Takes a GraphQL field config and checks to see if the `@semanticNonNull`
189-
* directive was applied; if so, converts to a field config using explicit
190-
* GraphQLSemanticNonNull wrapper types instead.
191-
*
192-
* @see {@url https://www.apollographql.com/docs/kotlin/advanced/nullability/#semanticnonnull}
193-
*/
194-
export function applySemanticNonNullDirectiveToFieldConfig(
195-
spec: GraphQLFieldConfig<unknown, unknown, unknown>,
196-
): GraphQLFieldConfig<unknown, unknown, unknown> {
197-
const directive = spec.astNode?.directives?.find(
198-
(d) => d.name.value === "semanticNonNull",
199-
);
200-
if (!directive) {
201-
return spec;
202-
}
203-
const levelsArg = directive.arguments?.find((a) => a.name.value === "levels");
204-
const levels =
205-
levelsArg?.value?.kind === Kind.LIST
206-
? levelsArg.value.values
207-
.filter((v) => v.kind === Kind.INT)
208-
.map((v) => Number(v.value))
209-
: [0];
210-
function recurse(type: GraphQLOutputType, level: number): GraphQLOutputType {
211-
if (type instanceof GraphQLSemanticNonNull) {
212-
// Strip semantic-non-null types; this should never happen but if someone
213-
// uses both semantic-non-null and the `@semanticNonNull` directive, we
214-
// want the directive to win (I guess?)
215-
return recurse(type.ofType, level);
216-
} else if (type instanceof GraphQLNonNull) {
217-
const inner = recurse(type.ofType, level);
218-
if (levels.includes(level)) {
219-
// Semantic non-null from `inner` replaces our GrpahQLNonNull wrapper
220-
return inner;
221-
} else {
222-
// Keep non-null wrapper; no semantic-non-null was added to `inner`
223-
return new GraphQLNonNull(inner);
224-
}
225-
} else if (type instanceof GraphQLList) {
226-
const inner = new GraphQLList(recurse(type.ofType, level + 1));
227-
if (levels.includes(level)) {
228-
return new GraphQLSemanticNonNull(inner);
229-
} else {
230-
return inner;
231-
}
232-
} else {
233-
if (levels.includes(level)) {
234-
return new GraphQLSemanticNonNull(type);
235-
} else {
236-
return type;
237-
}
238-
}
239-
}
240-
241-
return {
242-
...spec,
243-
type: recurse(spec.type, 0),
244-
astNode: spec.astNode
245-
? {
246-
...spec.astNode,
247-
directives: spec.astNode.directives?.filter(
248-
(d) => d.name.value !== "semanticNonNull",
249-
),
250-
}
251-
: undefined,
252-
};
253-
}

0 commit comments

Comments
 (0)