joist-graphql-codegen 2.3.0-next.7 → 2.3.0-next.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/_virtual/_rolldown/runtime.cjs +23 -0
- package/build/generateEnumDetailResolvers.cjs +40 -0
- package/build/generateEnumDetailResolvers.cjs.map +1 -0
- package/build/generateEnumDetailResolvers.d.cts +8 -0
- package/build/generateEnumDetailResolvers.d.cts.map +1 -0
- package/build/generateEnumDetailResolvers.d.mts +8 -0
- package/build/generateEnumDetailResolvers.d.mts.map +1 -0
- package/build/generateEnumDetailResolvers.js +23 -23
- package/build/generateEnumDetailResolvers.js.map +1 -1
- package/build/generateEnumsGraphql.cjs +29 -0
- package/build/generateEnumsGraphql.cjs.map +1 -0
- package/build/generateEnumsGraphql.d.cts +8 -0
- package/build/generateEnumsGraphql.d.cts.map +1 -0
- package/build/generateEnumsGraphql.d.mts +8 -0
- package/build/generateEnumsGraphql.d.mts.map +1 -0
- package/build/generateEnumsGraphql.js +24 -27
- package/build/generateEnumsGraphql.js.map +1 -1
- package/build/generateGraphqlCodegen.cjs +54 -0
- package/build/generateGraphqlCodegen.cjs.map +1 -0
- package/build/generateGraphqlCodegen.d.cts +8 -0
- package/build/generateGraphqlCodegen.d.cts.map +1 -0
- package/build/generateGraphqlCodegen.d.mts +8 -0
- package/build/generateGraphqlCodegen.d.mts.map +1 -0
- package/build/generateGraphqlCodegen.js +28 -30
- package/build/generateGraphqlCodegen.js.map +1 -1
- package/build/generateGraphqlSchemaFiles.cjs +491 -0
- package/build/generateGraphqlSchemaFiles.cjs.map +1 -0
- package/build/{generateGraphqlSchemaFiles.d.ts → generateGraphqlSchemaFiles.d.cts} +6 -3
- package/build/generateGraphqlSchemaFiles.d.cts.map +1 -0
- package/build/generateGraphqlSchemaFiles.d.mts +15 -0
- package/build/generateGraphqlSchemaFiles.d.mts.map +1 -0
- package/build/generateGraphqlSchemaFiles.js +444 -286
- package/build/generateGraphqlSchemaFiles.js.map +1 -1
- package/build/generateObjectResolvers.cjs +70 -0
- package/build/generateObjectResolvers.cjs.map +1 -0
- package/build/{generateObjectResolvers.d.ts → generateObjectResolvers.d.cts} +5 -2
- package/build/generateObjectResolvers.d.cts.map +1 -0
- package/build/generateObjectResolvers.d.mts +17 -0
- package/build/generateObjectResolvers.d.mts.map +1 -0
- package/build/generateObjectResolvers.js +48 -38
- package/build/generateObjectResolvers.js.map +1 -1
- package/build/generateQueryPageResolvers.cjs +51 -0
- package/build/generateQueryPageResolvers.cjs.map +1 -0
- package/build/generateQueryPageResolvers.d.cts +8 -0
- package/build/generateQueryPageResolvers.d.cts.map +1 -0
- package/build/generateQueryPageResolvers.d.mts +8 -0
- package/build/generateQueryPageResolvers.d.mts.map +1 -0
- package/build/generateQueryPageResolvers.js +31 -35
- package/build/generateQueryPageResolvers.js.map +1 -1
- package/build/generateQueryResolvers.cjs +55 -0
- package/build/generateQueryResolvers.cjs.map +1 -0
- package/build/generateQueryResolvers.d.cts +10 -0
- package/build/generateQueryResolvers.d.cts.map +1 -0
- package/build/generateQueryResolvers.d.mts +10 -0
- package/build/generateQueryResolvers.d.mts.map +1 -0
- package/build/generateQueryResolvers.js +38 -36
- package/build/generateQueryResolvers.js.map +1 -1
- package/build/generateResolverUtils.cjs +36 -0
- package/build/generateResolverUtils.cjs.map +1 -0
- package/build/generateResolverUtils.d.cts +8 -0
- package/build/generateResolverUtils.d.cts.map +1 -0
- package/build/generateResolverUtils.d.mts +8 -0
- package/build/generateResolverUtils.d.mts.map +1 -0
- package/build/generateResolverUtils.js +25 -26
- package/build/generateResolverUtils.js.map +1 -1
- package/build/generateSaveResolvers.cjs +57 -0
- package/build/generateSaveResolvers.cjs.map +1 -0
- package/build/{generateSaveResolvers.d.ts → generateSaveResolvers.d.cts} +5 -2
- package/build/generateSaveResolvers.d.cts.map +1 -0
- package/build/generateSaveResolvers.d.mts +17 -0
- package/build/generateSaveResolvers.d.mts.map +1 -0
- package/build/generateSaveResolvers.js +39 -40
- package/build/generateSaveResolvers.js.map +1 -1
- package/build/graphqlUtils.cjs +160 -0
- package/build/graphqlUtils.cjs.map +1 -0
- package/build/graphqlUtils.d.cts +31 -0
- package/build/graphqlUtils.d.cts.map +1 -0
- package/build/graphqlUtils.d.mts +31 -0
- package/build/graphqlUtils.d.mts.map +1 -0
- package/build/graphqlUtils.js +120 -218
- package/build/graphqlUtils.js.map +1 -1
- package/build/history.cjs +25 -0
- package/build/history.cjs.map +1 -0
- package/build/history.d.cts +9 -0
- package/build/history.d.cts.map +1 -0
- package/build/history.d.mts +9 -0
- package/build/history.d.mts.map +1 -0
- package/build/history.js +17 -14
- package/build/history.js.map +1 -1
- package/build/index.cjs +57 -0
- package/build/index.cjs.map +1 -0
- package/build/index.d.cts +7 -0
- package/build/index.d.cts.map +1 -0
- package/build/index.d.mts +7 -0
- package/build/index.d.mts.map +1 -0
- package/build/index.js +48 -54
- package/build/index.js.map +1 -1
- package/build/testUtils.cjs +191 -0
- package/build/testUtils.cjs.map +1 -0
- package/build/testUtils.d.cts +20 -0
- package/build/testUtils.d.cts.map +1 -0
- package/build/testUtils.d.mts +20 -0
- package/build/testUtils.d.mts.map +1 -0
- package/build/testUtils.js +162 -158
- package/build/testUtils.js.map +1 -1
- package/build/utils.cjs +51 -0
- package/build/utils.cjs.map +1 -0
- package/build/utils.d.cts +16 -0
- package/build/utils.d.cts.map +1 -0
- package/build/utils.d.mts +16 -0
- package/build/utils.d.mts.map +1 -0
- package/build/utils.js +37 -49
- package/build/utils.js.map +1 -1
- package/package.json +33 -13
- package/build/generateEnumDetailResolvers.d.ts +0 -5
- package/build/generateEnumDetailResolvers.d.ts.map +0 -1
- package/build/generateEnumDetailResolvers.test.d.ts +0 -2
- package/build/generateEnumDetailResolvers.test.d.ts.map +0 -1
- package/build/generateEnumDetailResolvers.test.js +0 -34
- package/build/generateEnumDetailResolvers.test.js.map +0 -1
- package/build/generateEnumsGraphql.d.ts +0 -5
- package/build/generateEnumsGraphql.d.ts.map +0 -1
- package/build/generateGraphqlCodegen.d.ts +0 -5
- package/build/generateGraphqlCodegen.d.ts.map +0 -1
- package/build/generateGraphqlCodegen.test.d.ts +0 -2
- package/build/generateGraphqlCodegen.test.d.ts.map +0 -1
- package/build/generateGraphqlCodegen.test.js +0 -44
- package/build/generateGraphqlCodegen.test.js.map +0 -1
- package/build/generateGraphqlSchemaFiles.d.ts.map +0 -1
- package/build/generateGraphqlSchemaFiles.test.d.ts +0 -2
- package/build/generateGraphqlSchemaFiles.test.d.ts.map +0 -1
- package/build/generateGraphqlSchemaFiles.test.js +0 -1008
- package/build/generateGraphqlSchemaFiles.test.js.map +0 -1
- package/build/generateObjectResolvers.d.ts.map +0 -1
- package/build/generateQueryPageResolvers.d.ts +0 -5
- package/build/generateQueryPageResolvers.d.ts.map +0 -1
- package/build/generateQueryPageResolvers.test.d.ts +0 -2
- package/build/generateQueryPageResolvers.test.d.ts.map +0 -1
- package/build/generateQueryPageResolvers.test.js +0 -46
- package/build/generateQueryPageResolvers.test.js.map +0 -1
- package/build/generateQueryResolvers.d.ts +0 -7
- package/build/generateQueryResolvers.d.ts.map +0 -1
- package/build/generateQueryResolvers.test.d.ts +0 -2
- package/build/generateQueryResolvers.test.d.ts.map +0 -1
- package/build/generateQueryResolvers.test.js +0 -45
- package/build/generateQueryResolvers.test.js.map +0 -1
- package/build/generateResolverUtils.d.ts +0 -5
- package/build/generateResolverUtils.d.ts.map +0 -1
- package/build/generateResolverUtils.test.d.ts +0 -2
- package/build/generateResolverUtils.test.d.ts.map +0 -1
- package/build/generateResolverUtils.test.js +0 -53
- package/build/generateResolverUtils.test.js.map +0 -1
- package/build/generateSaveResolvers.d.ts.map +0 -1
- package/build/generateSaveResolvers.test.d.ts +0 -2
- package/build/generateSaveResolvers.test.d.ts.map +0 -1
- package/build/generateSaveResolvers.test.js +0 -49
- package/build/generateSaveResolvers.test.js.map +0 -1
- package/build/graphqlUtils.d.ts +0 -28
- package/build/graphqlUtils.d.ts.map +0 -1
- package/build/history.d.ts +0 -6
- package/build/history.d.ts.map +0 -1
- package/build/index.d.ts +0 -4
- package/build/index.d.ts.map +0 -1
- package/build/testUtils.d.ts +0 -25
- package/build/testUtils.d.ts.map +0 -1
- package/build/utils.d.ts +0 -13
- package/build/utils.d.ts.map +0 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
require("./utils.cjs");
|
|
4
|
+
require("joist-codegen");
|
|
5
|
+
let ts_poet = require("ts-poet");
|
|
6
|
+
let joist_utils = require("joist-utils");
|
|
7
|
+
let graphql = require("graphql");
|
|
8
|
+
let prettier = require("prettier");
|
|
9
|
+
prettier = require_runtime.__toESM(prettier, 1);
|
|
10
|
+
//#region src/graphqlUtils.ts
|
|
11
|
+
/** Given a `file` and `fields` to go in it (for potentially different objects), upserts into the existing types. */
|
|
12
|
+
async function upsertIntoFile(fs, file, fields) {
|
|
13
|
+
const byObjectName = (0, joist_utils.groupBy)(fields, (f) => f.objectName);
|
|
14
|
+
const existingDoc = parseOrNewEmptyDoc(await fs.load(file));
|
|
15
|
+
const newDocs = Object.entries(byObjectName).map(([objectName, fields]) => {
|
|
16
|
+
const [entry] = fields;
|
|
17
|
+
return [objectName, entry.objectType === "union" ? createUnionDoc(entry) : createFieldDoc(objectName, fields)];
|
|
18
|
+
});
|
|
19
|
+
const formatted = await formatGraphQL((0, graphql.print)(mergeDocs(existingDoc, newDocs)));
|
|
20
|
+
await fs.save(file, formatted);
|
|
21
|
+
}
|
|
22
|
+
let prettierPromise;
|
|
23
|
+
async function formatGraphQL(content) {
|
|
24
|
+
const prettierConfig = await (prettierPromise ??= (0, prettier.resolveConfig)("./"));
|
|
25
|
+
return prettier.default.format(content, {
|
|
26
|
+
parser: "graphql",
|
|
27
|
+
...prettierConfig,
|
|
28
|
+
plugins: []
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new AST DocumentNode for the given object + field definitions.
|
|
33
|
+
*
|
|
34
|
+
* This leverages `parse` to do the dirty work of taking an "easy for us to create"
|
|
35
|
+
* string of GraphQL SDL and turning it into a "pita to create by hand" AST
|
|
36
|
+
* of object / field / type / etc. nodes.
|
|
37
|
+
*/
|
|
38
|
+
function createFieldDoc(objectName, fields) {
|
|
39
|
+
const [{ objectType }] = fields;
|
|
40
|
+
const type = objectType === "output" ? "type" : objectType;
|
|
41
|
+
const maybeArgs = (f) => f.argsString ? `(${f.argsString})` : "";
|
|
42
|
+
const source = `${fields.some((f) => f.extends) ? "extend " : ""}${type} ${objectName} {
|
|
43
|
+
${fields.map((f) => `${f.fieldName}${maybeArgs(f)}: ${f.fieldType}`)}
|
|
44
|
+
}`;
|
|
45
|
+
return (0, graphql.parse)(source);
|
|
46
|
+
}
|
|
47
|
+
function createUnionDoc(union) {
|
|
48
|
+
const { objectName, types } = union;
|
|
49
|
+
return (0, graphql.parse)(`union ${objectName} = ${types.join(" | ")}`);
|
|
50
|
+
}
|
|
51
|
+
function parseOrNewEmptyDoc(content) {
|
|
52
|
+
return content ? (0, graphql.parse)(content) : newEmptyDocument();
|
|
53
|
+
}
|
|
54
|
+
function newEmptyDocument() {
|
|
55
|
+
return (0, graphql.visit)((0, graphql.parse)("type Foo"), { ObjectTypeDefinition() {
|
|
56
|
+
return null;
|
|
57
|
+
} });
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Merges a given `newType` from `newDoc` into `existingDoc*.
|
|
61
|
+
*
|
|
62
|
+
* There are existing "merge schema" libraries/tools out there, but we want to be very purposeful
|
|
63
|
+
* about keeping the existing SDL and this is really not too bad because we're only stitching
|
|
64
|
+
* ~two levels of the AST (document -> objects and object -> fields).
|
|
65
|
+
*
|
|
66
|
+
* We also cheat by using `createNewDoc` to get ASTs of the new content, so we don't have
|
|
67
|
+
* to laboriously built up the ASTs by hand.
|
|
68
|
+
*/
|
|
69
|
+
function mergeDocs(existingDoc, newDocs) {
|
|
70
|
+
return (0, graphql.visit)(existingDoc, {
|
|
71
|
+
Document(node) {
|
|
72
|
+
return {
|
|
73
|
+
...node,
|
|
74
|
+
definitions: [...node.definitions, ...newDocs.filter(([objectType]) => {
|
|
75
|
+
return !node.definitions.some((d) => (d.kind === "ObjectTypeDefinition" || d.kind === "ObjectTypeExtension" || d.kind === "InputObjectTypeDefinition" || d.kind === "UnionTypeDefinition") && d.name.value === objectType);
|
|
76
|
+
}).flatMap(([, d]) => d.definitions)].sort(byOperationExtensionsFirst)
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
ObjectTypeDefinition(node) {
|
|
80
|
+
return mergeObjectTypeFields(node, newDocs);
|
|
81
|
+
},
|
|
82
|
+
ObjectTypeExtension(node) {
|
|
83
|
+
return mergeObjectTypeFields(node, newDocs);
|
|
84
|
+
},
|
|
85
|
+
InputObjectTypeDefinition(node) {
|
|
86
|
+
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
87
|
+
if (found) {
|
|
88
|
+
const [, newDoc] = found;
|
|
89
|
+
const existingFieldNames = (node.fields || []).map((f) => f.name.value);
|
|
90
|
+
const newObjectType = newDoc.definitions[0];
|
|
91
|
+
return {
|
|
92
|
+
...node,
|
|
93
|
+
fields: [...node.fields || [], ...(newObjectType.fields || []).filter((f) => !existingFieldNames.includes(f.name.value))]
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return node;
|
|
97
|
+
},
|
|
98
|
+
UnionTypeDefinition(node) {
|
|
99
|
+
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
100
|
+
if (found) {
|
|
101
|
+
const [, newDoc] = found;
|
|
102
|
+
const [definition] = newDoc.definitions;
|
|
103
|
+
return {
|
|
104
|
+
...node,
|
|
105
|
+
types: definition.types
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return node;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/** Merges generated fields into an existing `type` or `extend type`. */
|
|
113
|
+
function mergeObjectTypeFields(node, newDocs) {
|
|
114
|
+
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
115
|
+
if (found) {
|
|
116
|
+
const [, newDoc] = found;
|
|
117
|
+
const existingFieldNames = (node.fields || []).map((f) => f.name.value);
|
|
118
|
+
const newObjectType = newDoc.definitions[0];
|
|
119
|
+
return {
|
|
120
|
+
...node,
|
|
121
|
+
fields: [...node.fields || [], ...(newObjectType.fields || []).filter((f) => !existingFieldNames.includes(f.name.value))]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return node;
|
|
125
|
+
}
|
|
126
|
+
/** Keeps the primary operation extensions at the top of generated schema files. */
|
|
127
|
+
function byOperationExtensionsFirst(a, b) {
|
|
128
|
+
return operationExtensionRank(a) - operationExtensionRank(b);
|
|
129
|
+
}
|
|
130
|
+
/** Returns the priority for placing generated operation extensions before types/inputs. */
|
|
131
|
+
function operationExtensionRank(definition) {
|
|
132
|
+
if (definition.kind === "ObjectTypeExtension" && definition.name.value === "Query") return 0;
|
|
133
|
+
if (definition.kind === "ObjectTypeExtension" && definition.name.value === "Mutation") return 1;
|
|
134
|
+
return 2;
|
|
135
|
+
}
|
|
136
|
+
function mapTypescriptTypeToGraphQLType(fieldName, type) {
|
|
137
|
+
if (type === "string") return "String";
|
|
138
|
+
else if (type === "boolean") return "Boolean";
|
|
139
|
+
else if (type === "number") return "Int";
|
|
140
|
+
else if (type === "bigint") return "BigInt";
|
|
141
|
+
else if (type instanceof ts_poet.Code) {
|
|
142
|
+
const rawType = type.toCodeString([]);
|
|
143
|
+
if (rawType.startsWith("Date")) return fieldName.endsWith("At") ? "DateTime" : "Date";
|
|
144
|
+
else if (rawType.startsWith("Temporal.ZonedDateTime")) return "DateTime";
|
|
145
|
+
else if (rawType.startsWith("Temporal.PlainDateTime")) return "DateTime";
|
|
146
|
+
else if (rawType.startsWith("Temporal.PlainDate")) return "Date";
|
|
147
|
+
else return;
|
|
148
|
+
} else if (type instanceof ts_poet.Import) return;
|
|
149
|
+
else return;
|
|
150
|
+
}
|
|
151
|
+
function isJsonbColumn(p) {
|
|
152
|
+
return p.fieldType === "Object" || p.fieldType instanceof ts_poet.Import;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
exports.formatGraphQL = formatGraphQL;
|
|
156
|
+
exports.isJsonbColumn = isJsonbColumn;
|
|
157
|
+
exports.mapTypescriptTypeToGraphQLType = mapTypescriptTypeToGraphQLType;
|
|
158
|
+
exports.upsertIntoFile = upsertIntoFile;
|
|
159
|
+
|
|
160
|
+
//# sourceMappingURL=graphqlUtils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphqlUtils.cjs","names":["groupBy","print","resolveConfig","parse","visit","Code","Import"],"sources":["../src/graphqlUtils.ts"],"sourcesContent":["import {\n type DocumentNode,\n type InputObjectTypeDefinitionNode,\n type ObjectTypeDefinitionNode,\n type ObjectTypeExtensionNode,\n type UnionTypeDefinitionNode,\n parse,\n print,\n visit,\n} from \"graphql\";\nimport { type PrimitiveField, type PrimitiveTypescriptType } from \"joist-codegen\";\nimport { groupBy } from \"joist-utils\";\nimport prettier, { type Options, resolveConfig } from \"prettier\";\nimport { Code, Import } from \"ts-poet\";\n\nimport { type Fs } from \"./utils.ts\";\n\n/** A type for the fields we want to add to `*.graphql` files. */\nexport type GqlField = {\n file: string;\n objectType: \"input\" | \"output\";\n objectName: string;\n fieldName: string;\n fieldType: string;\n // Not a true AST, but just `foo: Bar, zaz: number`\n argsString?: string;\n extends?: boolean;\n};\n\nexport type GqlUnion = { file: string; objectName: string; objectType: \"union\"; types: string[] };\n\nexport type GqlEntry = GqlField | GqlUnion;\n\n/** Given a `file` and `fields` to go in it (for potentially different objects), upserts into the existing types. */\nexport async function upsertIntoFile(fs: Fs, file: string, fields: GqlEntry[]): Promise<void> {\n // Group the fields by each individual object (either input types or output types)\n const byObjectName = groupBy(fields, (f) => f.objectName);\n const existingDoc = parseOrNewEmptyDoc(await fs.load(file));\n\n // For each type, create a \"new\" / ideal definition that exactly matches the domain model\n const newDocs = Object.entries(byObjectName).map(([objectName, fields]) => {\n const [entry] = fields;\n return [\n objectName,\n entry.objectType === \"union\"\n ? createUnionDoc(entry as GqlUnion)\n : createFieldDoc(objectName, fields as GqlField[]),\n ] as [string, DocumentNode];\n });\n\n // Merge each `newDoc` object definition into the file's existing types\n const content = print(mergeDocs(existingDoc, newDocs));\n const formatted = await formatGraphQL(content);\n\n await fs.save(file, formatted);\n}\n\nlet prettierPromise: Promise<Options | null>;\n\nexport async function formatGraphQL(content: string): Promise<string> {\n const prettierConfig = await (prettierPromise ??= resolveConfig(\"./\"));\n return prettier.format(content, {\n parser: \"graphql\",\n ...prettierConfig,\n plugins: [],\n });\n}\n\n/**\n * Creates a new AST DocumentNode for the given object + field definitions.\n *\n * This leverages `parse` to do the dirty work of taking an \"easy for us to create\"\n * string of GraphQL SDL and turning it into a \"pita to create by hand\" AST\n * of object / field / type / etc. nodes.\n */\nfunction createFieldDoc(objectName: string, fields: GqlField[]): DocumentNode {\n const [{ objectType }] = fields;\n const type = objectType === \"output\" ? \"type\" : objectType;\n const maybeArgs = (f: GqlField) => (f.argsString ? `(${f.argsString})` : \"\");\n const maybeExtends = fields.some((f: GqlField) => f.extends) ? \"extend \" : \"\";\n const source = `${maybeExtends}${type} ${objectName} {\n ${fields.map((f) => `${f.fieldName}${maybeArgs(f)}: ${f.fieldType}`)}\n }`;\n return parse(source);\n}\n\nfunction createUnionDoc(union: GqlUnion): DocumentNode {\n const { objectName, types } = union;\n return parse(`union ${objectName} = ${types.join(\" | \")}`);\n}\n\nfunction parseOrNewEmptyDoc(content: string | undefined): DocumentNode {\n return content ? parse(content) : newEmptyDocument();\n}\n\nfunction newEmptyDocument(): DocumentNode {\n // I can't figure out how to `parse(\"\")`, i.e. create an empty DocumentNode, so pretend\n // to make a `type Foo` but then throw it away so that DocumentNode ends up empty.\n return visit(parse(\"type Foo\"), {\n ObjectTypeDefinition() {\n return null;\n },\n });\n}\n\n/**\n * Merges a given `newType` from `newDoc` into `existingDoc*.\n *\n * There are existing \"merge schema\" libraries/tools out there, but we want to be very purposeful\n * about keeping the existing SDL and this is really not too bad because we're only stitching\n * ~two levels of the AST (document -> objects and object -> fields).\n *\n * We also cheat by using `createNewDoc` to get ASTs of the new content, so we don't have\n * to laboriously built up the ASTs by hand.\n */\nfunction mergeDocs(existingDoc: DocumentNode, newDocs: [string, DocumentNode][]): DocumentNode {\n // Now merged the two\n return visit(existingDoc, {\n // If the `type Book` does not exist yet, add it\n Document(node) {\n return {\n ...node,\n definitions: [\n ...node.definitions,\n ...newDocs\n .filter(([objectType]) => {\n return !node.definitions.some(\n (d) =>\n (d.kind === \"ObjectTypeDefinition\" ||\n d.kind === \"ObjectTypeExtension\" ||\n d.kind === \"InputObjectTypeDefinition\" ||\n d.kind === \"UnionTypeDefinition\") &&\n d.name.value === objectType,\n );\n })\n .flatMap(([, d]) => d.definitions),\n ].sort(byOperationExtensionsFirst),\n };\n },\n\n // If fields don't exist yet, add them\n ObjectTypeDefinition(node) {\n return mergeObjectTypeFields(node, newDocs);\n },\n\n ObjectTypeExtension(node) {\n return mergeObjectTypeFields(node, newDocs);\n },\n\n // If fields don't exist yet, add them\n InputObjectTypeDefinition(node) {\n const found = newDocs.find(([objectType]) => node.name.value === objectType);\n if (found) {\n const [, newDoc] = found;\n const existingFieldNames = (node.fields || []).map((f) => f.name.value);\n const newObjectType = newDoc.definitions[0] as InputObjectTypeDefinitionNode;\n return {\n ...node,\n fields: [\n ...(node.fields || []),\n ...(newObjectType.fields || []).filter((f) => !existingFieldNames.includes(f.name.value)),\n ],\n };\n }\n return node;\n },\n\n UnionTypeDefinition(node) {\n const found = newDocs.find(([objectType]) => node.name.value === objectType);\n if (found) {\n const [, newDoc] = found;\n const [definition] = newDoc.definitions as UnionTypeDefinitionNode[];\n return {\n ...node,\n types: definition.types,\n };\n }\n return node;\n },\n });\n}\n\n/** Merges generated fields into an existing `type` or `extend type`. */\nfunction mergeObjectTypeFields<T extends ObjectTypeDefinitionNode | ObjectTypeExtensionNode>(\n node: T,\n newDocs: [string, DocumentNode][],\n): T {\n const found = newDocs.find(([objectType]) => node.name.value === objectType);\n if (found) {\n const [, newDoc] = found;\n const existingFieldNames = (node.fields || []).map((f) => f.name.value);\n const newObjectType = newDoc.definitions[0] as ObjectTypeDefinitionNode;\n return {\n ...node,\n fields: [\n ...(node.fields || []),\n ...(newObjectType.fields || []).filter((f) => !existingFieldNames.includes(f.name.value)),\n ],\n };\n }\n return node;\n}\n\n/** Keeps the primary operation extensions at the top of generated schema files. */\nfunction byOperationExtensionsFirst(\n a: DocumentNode[\"definitions\"][number],\n b: DocumentNode[\"definitions\"][number],\n): number {\n return operationExtensionRank(a) - operationExtensionRank(b);\n}\n\n/** Returns the priority for placing generated operation extensions before types/inputs. */\nfunction operationExtensionRank(definition: DocumentNode[\"definitions\"][number]): number {\n if (definition.kind === \"ObjectTypeExtension\" && definition.name.value === \"Query\") return 0;\n if (definition.kind === \"ObjectTypeExtension\" && definition.name.value === \"Mutation\") return 1;\n return 2;\n}\n\nexport type GraphQLType = \"BigInt\" | \"Boolean\" | \"String\" | \"Int\" | \"Date\" | \"DateTime\" | string;\n\nexport type SupportedTypescriptTypes = Exclude<PrimitiveTypescriptType, \"Object\" | Import>;\n\nexport function mapTypescriptTypeToGraphQLType(\n fieldName: string,\n type: PrimitiveTypescriptType | Import | Code,\n): GraphQLType | undefined {\n if (type === \"string\") {\n return \"String\";\n } else if (type === \"boolean\") {\n return \"Boolean\";\n } else if (type === \"number\") {\n return \"Int\";\n } else if (type === \"bigint\") {\n return \"BigInt\";\n } else if (type instanceof Code) {\n const rawType = type.toCodeString([]);\n if (rawType.startsWith(\"Date\")) {\n // Legacy javascript Date doesn't have different `date` vs. `datetime` types but GraphQL does\n // so lean on the `..._at` suffix convention to know \"DateTime\".\n return fieldName.endsWith(\"At\") ? \"DateTime\" : \"Date\";\n } else if (rawType.startsWith(\"Temporal.ZonedDateTime\")) {\n return \"DateTime\";\n } else if (rawType.startsWith(\"Temporal.PlainDateTime\")) {\n return \"DateTime\";\n } else if (rawType.startsWith(\"Temporal.PlainDate\")) {\n return \"Date\";\n } else {\n // If this is a fancy import like a superstruct/something, we can't guess what it will be in GraphQL\n return undefined;\n }\n } else if (type instanceof Import) {\n // If this is a fancy import like a superstruct/something, we can't guess what it will be in GraphQL\n return undefined;\n } else {\n // Anything else like `jsonb` (which shows up as `Object`) is also unlikely to be a valid\n // GraphQL type, and doing things like `someField: Object` w/o predefined scalar types will\n // cause GraphQL parsing errors.\n return undefined;\n }\n}\n\nexport function isJsonbColumn(p: PrimitiveField) {\n return p.fieldType === \"Object\" || p.fieldType instanceof Import;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Fs } from "./utils.cjs";
|
|
2
|
+
import { PrimitiveField, PrimitiveTypescriptType } from "joist-codegen";
|
|
3
|
+
import { Code, Import } from "ts-poet";
|
|
4
|
+
//#region src/graphqlUtils.d.ts
|
|
5
|
+
/** A type for the fields we want to add to `*.graphql` files. */
|
|
6
|
+
type GqlField = {
|
|
7
|
+
file: string;
|
|
8
|
+
objectType: "input" | "output";
|
|
9
|
+
objectName: string;
|
|
10
|
+
fieldName: string;
|
|
11
|
+
fieldType: string;
|
|
12
|
+
argsString?: string;
|
|
13
|
+
extends?: boolean;
|
|
14
|
+
};
|
|
15
|
+
type GqlUnion = {
|
|
16
|
+
file: string;
|
|
17
|
+
objectName: string;
|
|
18
|
+
objectType: "union";
|
|
19
|
+
types: string[];
|
|
20
|
+
};
|
|
21
|
+
type GqlEntry = GqlField | GqlUnion;
|
|
22
|
+
/** Given a `file` and `fields` to go in it (for potentially different objects), upserts into the existing types. */
|
|
23
|
+
declare function upsertIntoFile(fs: Fs, file: string, fields: GqlEntry[]): Promise<void>;
|
|
24
|
+
declare function formatGraphQL(content: string): Promise<string>;
|
|
25
|
+
type GraphQLType = "BigInt" | "Boolean" | "String" | "Int" | "Date" | "DateTime" | string;
|
|
26
|
+
type SupportedTypescriptTypes = Exclude<PrimitiveTypescriptType, "Object" | Import>;
|
|
27
|
+
declare function mapTypescriptTypeToGraphQLType(fieldName: string, type: PrimitiveTypescriptType | Import | Code): GraphQLType | undefined;
|
|
28
|
+
declare function isJsonbColumn(p: PrimitiveField): boolean;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { GqlEntry, GqlField, GqlUnion, GraphQLType, SupportedTypescriptTypes, formatGraphQL, isJsonbColumn, mapTypescriptTypeToGraphQLType, upsertIntoFile };
|
|
31
|
+
//# sourceMappingURL=graphqlUtils.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphqlUtils.d.cts","names":[],"sources":["../src/graphqlUtils.ts"],"mappings":";;;;;KAkBY;EACV;EACA;EACA;EACA;EACA;EAEA;EACA;;KAGU;EAAa;EAAc;EAAoB;EAAqB;;KAEpE,WAAW,WAAW;;iBAGZ,eAAe,IAAI,IAAI,cAAc,QAAQ,aAAa;iBAyB1D,cAAc,kBAAkB;KA+J1C;KAEA,2BAA2B,QAAQ,oCAAoC;iBAEnE,+BACd,mBACA,MAAM,0BAA0B,SAAS,OACxC;iBAoCa,cAAc,GAAG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Fs } from "./utils.mjs";
|
|
2
|
+
import { PrimitiveField, PrimitiveTypescriptType } from "joist-codegen";
|
|
3
|
+
import { Code, Import } from "ts-poet";
|
|
4
|
+
//#region src/graphqlUtils.d.ts
|
|
5
|
+
/** A type for the fields we want to add to `*.graphql` files. */
|
|
6
|
+
type GqlField = {
|
|
7
|
+
file: string;
|
|
8
|
+
objectType: "input" | "output";
|
|
9
|
+
objectName: string;
|
|
10
|
+
fieldName: string;
|
|
11
|
+
fieldType: string;
|
|
12
|
+
argsString?: string;
|
|
13
|
+
extends?: boolean;
|
|
14
|
+
};
|
|
15
|
+
type GqlUnion = {
|
|
16
|
+
file: string;
|
|
17
|
+
objectName: string;
|
|
18
|
+
objectType: "union";
|
|
19
|
+
types: string[];
|
|
20
|
+
};
|
|
21
|
+
type GqlEntry = GqlField | GqlUnion;
|
|
22
|
+
/** Given a `file` and `fields` to go in it (for potentially different objects), upserts into the existing types. */
|
|
23
|
+
declare function upsertIntoFile(fs: Fs, file: string, fields: GqlEntry[]): Promise<void>;
|
|
24
|
+
declare function formatGraphQL(content: string): Promise<string>;
|
|
25
|
+
type GraphQLType = "BigInt" | "Boolean" | "String" | "Int" | "Date" | "DateTime" | string;
|
|
26
|
+
type SupportedTypescriptTypes = Exclude<PrimitiveTypescriptType, "Object" | Import>;
|
|
27
|
+
declare function mapTypescriptTypeToGraphQLType(fieldName: string, type: PrimitiveTypescriptType | Import | Code): GraphQLType | undefined;
|
|
28
|
+
declare function isJsonbColumn(p: PrimitiveField): boolean;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { GqlEntry, GqlField, GqlUnion, GraphQLType, SupportedTypescriptTypes, formatGraphQL, isJsonbColumn, mapTypescriptTypeToGraphQLType, upsertIntoFile };
|
|
31
|
+
//# sourceMappingURL=graphqlUtils.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphqlUtils.d.mts","names":[],"sources":["../src/graphqlUtils.ts"],"mappings":";;;;;KAkBY;EACV;EACA;EACA;EACA;EACA;EAEA;EACA;;KAGU;EAAa;EAAc;EAAoB;EAAqB;;KAEpE,WAAW,WAAW;;iBAGZ,eAAe,IAAI,IAAI,cAAc,QAAQ,aAAa;iBAyB1D,cAAc,kBAAkB;KA+J1C;KAEA,2BAA2B,QAAQ,oCAAoC;iBAEnE,+BACd,mBACA,MAAM,0BAA0B,SAAS,OACxC;iBAoCa,cAAc,GAAG"}
|
package/build/graphqlUtils.js
CHANGED
|
@@ -1,252 +1,154 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.upsertIntoFile = upsertIntoFile;
|
|
37
|
-
exports.formatGraphQL = formatGraphQL;
|
|
38
|
-
exports.mapTypescriptTypeToGraphQLType = mapTypescriptTypeToGraphQLType;
|
|
39
|
-
exports.isJsonbColumn = isJsonbColumn;
|
|
40
|
-
const graphql_1 = require("graphql");
|
|
41
|
-
const joist_utils_1 = require("joist-utils");
|
|
42
|
-
const prettier_1 = __importStar(require("prettier"));
|
|
43
|
-
const ts_poet_1 = require("ts-poet");
|
|
1
|
+
import "./utils.js";
|
|
2
|
+
import "joist-codegen";
|
|
3
|
+
import { Code, Import } from "ts-poet";
|
|
4
|
+
import { groupBy } from "joist-utils";
|
|
5
|
+
import { parse, print, visit } from "graphql";
|
|
6
|
+
import prettier, { resolveConfig } from "prettier";
|
|
7
|
+
//#region src/graphqlUtils.ts
|
|
44
8
|
/** Given a `file` and `fields` to go in it (for potentially different objects), upserts into the existing types. */
|
|
45
9
|
async function upsertIntoFile(fs, file, fields) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
entry.objectType === "union"
|
|
55
|
-
? createUnionDoc(entry)
|
|
56
|
-
: createFieldDoc(objectName, fields),
|
|
57
|
-
];
|
|
58
|
-
});
|
|
59
|
-
// Merge each `newDoc` object definition into the file's existing types
|
|
60
|
-
const content = (0, graphql_1.print)(mergeDocs(existingDoc, newDocs));
|
|
61
|
-
const formatted = await formatGraphQL(content);
|
|
62
|
-
await fs.save(file, formatted);
|
|
10
|
+
const byObjectName = groupBy(fields, (f) => f.objectName);
|
|
11
|
+
const existingDoc = parseOrNewEmptyDoc(await fs.load(file));
|
|
12
|
+
const newDocs = Object.entries(byObjectName).map(([objectName, fields]) => {
|
|
13
|
+
const [entry] = fields;
|
|
14
|
+
return [objectName, entry.objectType === "union" ? createUnionDoc(entry) : createFieldDoc(objectName, fields)];
|
|
15
|
+
});
|
|
16
|
+
const formatted = await formatGraphQL(print(mergeDocs(existingDoc, newDocs)));
|
|
17
|
+
await fs.save(file, formatted);
|
|
63
18
|
}
|
|
64
19
|
let prettierPromise;
|
|
65
20
|
async function formatGraphQL(content) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
21
|
+
const prettierConfig = await (prettierPromise ??= resolveConfig("./"));
|
|
22
|
+
return prettier.format(content, {
|
|
23
|
+
parser: "graphql",
|
|
24
|
+
...prettierConfig,
|
|
25
|
+
plugins: []
|
|
26
|
+
});
|
|
72
27
|
}
|
|
73
28
|
/**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
29
|
+
* Creates a new AST DocumentNode for the given object + field definitions.
|
|
30
|
+
*
|
|
31
|
+
* This leverages `parse` to do the dirty work of taking an "easy for us to create"
|
|
32
|
+
* string of GraphQL SDL and turning it into a "pita to create by hand" AST
|
|
33
|
+
* of object / field / type / etc. nodes.
|
|
34
|
+
*/
|
|
80
35
|
function createFieldDoc(objectName, fields) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const source = `${maybeExtends}${type} ${objectName} {
|
|
36
|
+
const [{ objectType }] = fields;
|
|
37
|
+
const type = objectType === "output" ? "type" : objectType;
|
|
38
|
+
const maybeArgs = (f) => f.argsString ? `(${f.argsString})` : "";
|
|
39
|
+
const source = `${fields.some((f) => f.extends) ? "extend " : ""}${type} ${objectName} {
|
|
86
40
|
${fields.map((f) => `${f.fieldName}${maybeArgs(f)}: ${f.fieldType}`)}
|
|
87
41
|
}`;
|
|
88
|
-
|
|
42
|
+
return parse(source);
|
|
89
43
|
}
|
|
90
44
|
function createUnionDoc(union) {
|
|
91
|
-
|
|
92
|
-
|
|
45
|
+
const { objectName, types } = union;
|
|
46
|
+
return parse(`union ${objectName} = ${types.join(" | ")}`);
|
|
93
47
|
}
|
|
94
48
|
function parseOrNewEmptyDoc(content) {
|
|
95
|
-
|
|
49
|
+
return content ? parse(content) : newEmptyDocument();
|
|
96
50
|
}
|
|
97
51
|
function newEmptyDocument() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
ObjectTypeDefinition() {
|
|
102
|
-
return null;
|
|
103
|
-
},
|
|
104
|
-
});
|
|
52
|
+
return visit(parse("type Foo"), { ObjectTypeDefinition() {
|
|
53
|
+
return null;
|
|
54
|
+
} });
|
|
105
55
|
}
|
|
106
56
|
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
57
|
+
* Merges a given `newType` from `newDoc` into `existingDoc*.
|
|
58
|
+
*
|
|
59
|
+
* There are existing "merge schema" libraries/tools out there, but we want to be very purposeful
|
|
60
|
+
* about keeping the existing SDL and this is really not too bad because we're only stitching
|
|
61
|
+
* ~two levels of the AST (document -> objects and object -> fields).
|
|
62
|
+
*
|
|
63
|
+
* We also cheat by using `createNewDoc` to get ASTs of the new content, so we don't have
|
|
64
|
+
* to laboriously built up the ASTs by hand.
|
|
65
|
+
*/
|
|
116
66
|
function mergeDocs(existingDoc, newDocs) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
return node;
|
|
160
|
-
},
|
|
161
|
-
UnionTypeDefinition(node) {
|
|
162
|
-
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
163
|
-
if (found) {
|
|
164
|
-
const [, newDoc] = found;
|
|
165
|
-
const [definition] = newDoc.definitions;
|
|
166
|
-
return {
|
|
167
|
-
...node,
|
|
168
|
-
types: definition.types,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
return node;
|
|
172
|
-
},
|
|
173
|
-
});
|
|
67
|
+
return visit(existingDoc, {
|
|
68
|
+
Document(node) {
|
|
69
|
+
return {
|
|
70
|
+
...node,
|
|
71
|
+
definitions: [...node.definitions, ...newDocs.filter(([objectType]) => {
|
|
72
|
+
return !node.definitions.some((d) => (d.kind === "ObjectTypeDefinition" || d.kind === "ObjectTypeExtension" || d.kind === "InputObjectTypeDefinition" || d.kind === "UnionTypeDefinition") && d.name.value === objectType);
|
|
73
|
+
}).flatMap(([, d]) => d.definitions)].sort(byOperationExtensionsFirst)
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
ObjectTypeDefinition(node) {
|
|
77
|
+
return mergeObjectTypeFields(node, newDocs);
|
|
78
|
+
},
|
|
79
|
+
ObjectTypeExtension(node) {
|
|
80
|
+
return mergeObjectTypeFields(node, newDocs);
|
|
81
|
+
},
|
|
82
|
+
InputObjectTypeDefinition(node) {
|
|
83
|
+
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
84
|
+
if (found) {
|
|
85
|
+
const [, newDoc] = found;
|
|
86
|
+
const existingFieldNames = (node.fields || []).map((f) => f.name.value);
|
|
87
|
+
const newObjectType = newDoc.definitions[0];
|
|
88
|
+
return {
|
|
89
|
+
...node,
|
|
90
|
+
fields: [...node.fields || [], ...(newObjectType.fields || []).filter((f) => !existingFieldNames.includes(f.name.value))]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return node;
|
|
94
|
+
},
|
|
95
|
+
UnionTypeDefinition(node) {
|
|
96
|
+
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
97
|
+
if (found) {
|
|
98
|
+
const [, newDoc] = found;
|
|
99
|
+
const [definition] = newDoc.definitions;
|
|
100
|
+
return {
|
|
101
|
+
...node,
|
|
102
|
+
types: definition.types
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return node;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
174
108
|
}
|
|
175
109
|
/** Merges generated fields into an existing `type` or `extend type`. */
|
|
176
110
|
function mergeObjectTypeFields(node, newDocs) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
return node;
|
|
111
|
+
const found = newDocs.find(([objectType]) => node.name.value === objectType);
|
|
112
|
+
if (found) {
|
|
113
|
+
const [, newDoc] = found;
|
|
114
|
+
const existingFieldNames = (node.fields || []).map((f) => f.name.value);
|
|
115
|
+
const newObjectType = newDoc.definitions[0];
|
|
116
|
+
return {
|
|
117
|
+
...node,
|
|
118
|
+
fields: [...node.fields || [], ...(newObjectType.fields || []).filter((f) => !existingFieldNames.includes(f.name.value))]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return node;
|
|
191
122
|
}
|
|
192
123
|
/** Keeps the primary operation extensions at the top of generated schema files. */
|
|
193
124
|
function byOperationExtensionsFirst(a, b) {
|
|
194
|
-
|
|
125
|
+
return operationExtensionRank(a) - operationExtensionRank(b);
|
|
195
126
|
}
|
|
196
127
|
/** Returns the priority for placing generated operation extensions before types/inputs. */
|
|
197
128
|
function operationExtensionRank(definition) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return 1;
|
|
202
|
-
return 2;
|
|
129
|
+
if (definition.kind === "ObjectTypeExtension" && definition.name.value === "Query") return 0;
|
|
130
|
+
if (definition.kind === "ObjectTypeExtension" && definition.name.value === "Mutation") return 1;
|
|
131
|
+
return 2;
|
|
203
132
|
}
|
|
204
133
|
function mapTypescriptTypeToGraphQLType(fieldName, type) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const rawType = type.toCodeString([]);
|
|
219
|
-
if (rawType.startsWith("Date")) {
|
|
220
|
-
// Legacy javascript Date doesn't have different `date` vs. `datetime` types but GraphQL does
|
|
221
|
-
// so lean on the `..._at` suffix convention to know "DateTime".
|
|
222
|
-
return fieldName.endsWith("At") ? "DateTime" : "Date";
|
|
223
|
-
}
|
|
224
|
-
else if (rawType.startsWith("Temporal.ZonedDateTime")) {
|
|
225
|
-
return "DateTime";
|
|
226
|
-
}
|
|
227
|
-
else if (rawType.startsWith("Temporal.PlainDateTime")) {
|
|
228
|
-
return "DateTime";
|
|
229
|
-
}
|
|
230
|
-
else if (rawType.startsWith("Temporal.PlainDate")) {
|
|
231
|
-
return "Date";
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
// If this is a fancy import like a superstruct/something, we can't guess what it will be in GraphQL
|
|
235
|
-
return undefined;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
else if (type instanceof ts_poet_1.Import) {
|
|
239
|
-
// If this is a fancy import like a superstruct/something, we can't guess what it will be in GraphQL
|
|
240
|
-
return undefined;
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
// Anything else like `jsonb` (which shows up as `Object`) is also unlikely to be a valid
|
|
244
|
-
// GraphQL type, and doing things like `someField: Object` w/o predefined scalar types will
|
|
245
|
-
// cause GraphQL parsing errors.
|
|
246
|
-
return undefined;
|
|
247
|
-
}
|
|
134
|
+
if (type === "string") return "String";
|
|
135
|
+
else if (type === "boolean") return "Boolean";
|
|
136
|
+
else if (type === "number") return "Int";
|
|
137
|
+
else if (type === "bigint") return "BigInt";
|
|
138
|
+
else if (type instanceof Code) {
|
|
139
|
+
const rawType = type.toCodeString([]);
|
|
140
|
+
if (rawType.startsWith("Date")) return fieldName.endsWith("At") ? "DateTime" : "Date";
|
|
141
|
+
else if (rawType.startsWith("Temporal.ZonedDateTime")) return "DateTime";
|
|
142
|
+
else if (rawType.startsWith("Temporal.PlainDateTime")) return "DateTime";
|
|
143
|
+
else if (rawType.startsWith("Temporal.PlainDate")) return "Date";
|
|
144
|
+
else return;
|
|
145
|
+
} else if (type instanceof Import) return;
|
|
146
|
+
else return;
|
|
248
147
|
}
|
|
249
148
|
function isJsonbColumn(p) {
|
|
250
|
-
|
|
149
|
+
return p.fieldType === "Object" || p.fieldType instanceof Import;
|
|
251
150
|
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { formatGraphQL, isJsonbColumn, mapTypescriptTypeToGraphQLType, upsertIntoFile };
|
|
153
|
+
|
|
252
154
|
//# sourceMappingURL=graphqlUtils.js.map
|