@constructive-io/graphql-codegen 2.17.29 → 2.17.30
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/README.md +97 -0
- package/codegen.js +35 -9
- package/esm/codegen.js +35 -9
- package/esm/gql.js +333 -159
- package/esm/options.js +8 -2
- package/gql.d.ts +37 -6
- package/gql.js +333 -159
- package/options.d.ts +10 -0
- package/options.js +8 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -84,6 +84,103 @@ Endpoint introspection:
|
|
|
84
84
|
- Set `input.endpoint` and optional `input.headers`
|
|
85
85
|
- If your dev server routes by hostname, add `headers: { Host: 'meta8.localhost' }`
|
|
86
86
|
|
|
87
|
+
## Selection Options
|
|
88
|
+
|
|
89
|
+
Configure result field selections, mutation input style, and connection pagination shape.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
selection: {
|
|
93
|
+
defaultMutationModelFields?: string[]
|
|
94
|
+
modelFields?: Record<string, string[]>
|
|
95
|
+
mutationInputMode?: 'expanded' | 'model' | 'raw' | 'patchCollapsed'
|
|
96
|
+
connectionStyle?: 'nodes' | 'edges'
|
|
97
|
+
forceModelOutput?: boolean
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- `defaultMutationModelFields`
|
|
102
|
+
- Sets default fields selected from the object payload returned by mutations when the mutation exposes an OBJECT output.
|
|
103
|
+
- Example: `['id']` will select only the `id` from the returned model unless overridden per model.
|
|
104
|
+
|
|
105
|
+
- `modelFields`
|
|
106
|
+
- Per‑model overrides for returned object payload fields.
|
|
107
|
+
- Example: `{ domain: ['id','domain','subdomain'] }` selects those fields from the `domain` object output.
|
|
108
|
+
|
|
109
|
+
- `mutationInputMode`
|
|
110
|
+
- Controls how mutation variables and `input` are generated.
|
|
111
|
+
- `expanded`: one variable per input property; `input` is a flat object of those variables.
|
|
112
|
+
- `model`: one variable per property; variables are nested under the singular model key inside `input`.
|
|
113
|
+
- `raw`: a single `$input: <CreateXInput>!` variable passed directly as `input: $input`.
|
|
114
|
+
- `patchCollapsed`: a single `$patch: <ModelPatch>!` plus required locator(s) (e.g., `$id`), passed as `input: { id: $id, patch: $patch }`.
|
|
115
|
+
|
|
116
|
+
- `connectionStyle`
|
|
117
|
+
- Standardizes connection queries and nested many selections.
|
|
118
|
+
- `nodes`: emits `totalCount` and `nodes { ... }`.
|
|
119
|
+
- `edges`: emits `totalCount`, `pageInfo { ... }`, and `edges { cursor node { ... } }`.
|
|
120
|
+
|
|
121
|
+
- `forceModelOutput`
|
|
122
|
+
- When `true`, ensures the object payload is selected even if `defaultMutationModelFields` is empty, defaulting to `['id']`.
|
|
123
|
+
- Useful to avoid generating mutations that only return `clientMutationId`.
|
|
124
|
+
|
|
125
|
+
### Examples
|
|
126
|
+
|
|
127
|
+
Create mutation with raw input:
|
|
128
|
+
|
|
129
|
+
```graphql
|
|
130
|
+
mutation createDomain($input: CreateDomainInput!) {
|
|
131
|
+
createDomain(input: $input) {
|
|
132
|
+
domain { id, domain, subdomain }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Patch mutation with collapsed patch:
|
|
138
|
+
|
|
139
|
+
```graphql
|
|
140
|
+
mutation updateDomain($id: UUID!, $patch: DomainPatch!) {
|
|
141
|
+
updateDomain(input: { id: $id, patch: $patch }) {
|
|
142
|
+
domain { id }
|
|
143
|
+
clientMutationId
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Edges‑style connection query:
|
|
149
|
+
|
|
150
|
+
```graphql
|
|
151
|
+
query getDomainsPaginated($first: Int, $after: Cursor) {
|
|
152
|
+
domains(first: $first, after: $after) {
|
|
153
|
+
totalCount
|
|
154
|
+
pageInfo { hasNextPage hasPreviousPage endCursor startCursor }
|
|
155
|
+
edges { cursor node { id domain subdomain } }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Custom Scalars and Type Overrides
|
|
161
|
+
|
|
162
|
+
- When your schema exposes custom scalars that are not available in the printed SDL or differ across environments, you can configure both TypeScript scalar typings and GraphQL type names used in generated operations.
|
|
163
|
+
|
|
164
|
+
- Add these to your config object:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"scalars": {
|
|
169
|
+
"LaunchqlInternalTypeHostname": "string",
|
|
170
|
+
"PgpmInternalTypeHostname": "string"
|
|
171
|
+
},
|
|
172
|
+
"typeNameOverrides": {
|
|
173
|
+
"LaunchqlInternalTypeHostname": "String",
|
|
174
|
+
"PgpmInternalTypeHostname": "String"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- `scalars`: maps GraphQL scalar names to TypeScript types for `typescript`/`typescript-operations`/`typescript-graphql-request`/`typescript-react-query` plugins.
|
|
180
|
+
- `typeNameOverrides`: rewrites scalar names in generated GraphQL AST so variable definitions and input fields use compatible built‑in GraphQL types.
|
|
181
|
+
|
|
182
|
+
- These options are also available programmatically through `LaunchQLGenOptions`.
|
|
183
|
+
|
|
87
184
|
---
|
|
88
185
|
|
|
89
186
|
## Education and Tutorials
|
package/codegen.js
CHANGED
|
@@ -97,8 +97,8 @@ async function getIntrospectionFromEndpoint(endpoint, headers) {
|
|
|
97
97
|
const res = await client.request(q);
|
|
98
98
|
return res;
|
|
99
99
|
}
|
|
100
|
-
function generateKeyedObjFromGqlMap(gqlMap) {
|
|
101
|
-
const gen = (0, gql_1.generate)(gqlMap);
|
|
100
|
+
function generateKeyedObjFromGqlMap(gqlMap, selection, typeNameOverrides, typeIndex) {
|
|
101
|
+
const gen = (0, gql_1.generate)(gqlMap, selection, typeNameOverrides, typeIndex);
|
|
102
102
|
return Object.entries(gen).reduce((acc, [key, val]) => {
|
|
103
103
|
if (val?.ast)
|
|
104
104
|
acc[key] = (0, graphql_1.print)(val.ast);
|
|
@@ -134,11 +134,12 @@ async function writeOperationsDocuments(docs, dir, format, convention) {
|
|
|
134
134
|
await ensureDir(dir);
|
|
135
135
|
const index = [];
|
|
136
136
|
for (const key of Object.keys(docs)) {
|
|
137
|
-
const
|
|
137
|
+
const base = getFilename(key, convention);
|
|
138
|
+
const filename = base + (format === 'ts' ? '.ts' : '.gql');
|
|
138
139
|
if (format === 'ts') {
|
|
139
140
|
const code = `import gql from 'graphql-tag'\nexport const ${key} = gql\`\n${docs[key]}\n\``;
|
|
140
141
|
await writeFileUTF8((0, path_1.join)(dir, filename), code);
|
|
141
|
-
index.push(`export * from './${
|
|
142
|
+
index.push(`export * from './${base}'`);
|
|
142
143
|
}
|
|
143
144
|
else {
|
|
144
145
|
await writeFileUTF8((0, path_1.join)(dir, filename), docs[key]);
|
|
@@ -152,7 +153,10 @@ async function runCodegen(opts, cwd) {
|
|
|
152
153
|
input: { ...(options_1.defaultGraphQLCodegenOptions.input), ...(opts.input || {}) },
|
|
153
154
|
output: { ...(options_1.defaultGraphQLCodegenOptions.output), ...(opts.output || {}) },
|
|
154
155
|
documents: { ...(options_1.defaultGraphQLCodegenOptions.documents), ...(opts.documents || {}) },
|
|
155
|
-
features: { ...(options_1.defaultGraphQLCodegenOptions.features), ...(opts.features || {}) }
|
|
156
|
+
features: { ...(options_1.defaultGraphQLCodegenOptions.features), ...(opts.features || {}) },
|
|
157
|
+
selection: { ...(options_1.defaultGraphQLCodegenOptions.selection), ...(opts.selection || {}) },
|
|
158
|
+
scalars: { ...(options_1.defaultGraphQLCodegenOptions.scalars || {}), ...(opts.scalars || {}) },
|
|
159
|
+
typeNameOverrides: { ...(options_1.defaultGraphQLCodegenOptions.typeNameOverrides || {}), ...(opts.typeNameOverrides || {}) }
|
|
156
160
|
};
|
|
157
161
|
const root = (0, path_1.join)(cwd, options.output.root);
|
|
158
162
|
const typesFile = (0, path_1.join)(root, options.output.typesFile);
|
|
@@ -171,8 +175,9 @@ async function runCodegen(opts, cwd) {
|
|
|
171
175
|
const schema = hasEndpoint
|
|
172
176
|
? (0, graphql_1.buildClientSchema)(introspection)
|
|
173
177
|
: (0, graphql_1.buildSchema)(await readFileUTF8(schemaPath));
|
|
178
|
+
const typeIndex = buildTypeIndex(introspection);
|
|
174
179
|
if (options.features.emitOperations || options.features.emitSdk || options.features.emitReactQuery) {
|
|
175
|
-
docs = generateKeyedObjFromGqlMap(gqlMap);
|
|
180
|
+
docs = generateKeyedObjFromGqlMap(gqlMap, options.selection, options.typeNameOverrides, typeIndex);
|
|
176
181
|
}
|
|
177
182
|
if (options.features.emitOperations) {
|
|
178
183
|
await writeOperationsDocuments(docs, operationsDir, options.documents.format, options.documents.convention);
|
|
@@ -182,7 +187,7 @@ async function runCodegen(opts, cwd) {
|
|
|
182
187
|
filename: typesFile,
|
|
183
188
|
schema: schema,
|
|
184
189
|
documents: [],
|
|
185
|
-
config: {},
|
|
190
|
+
config: { scalars: options.scalars || {} },
|
|
186
191
|
plugins: [{ typescript: {} }],
|
|
187
192
|
pluginMap: { typescript: typescriptPlugin }
|
|
188
193
|
});
|
|
@@ -201,7 +206,7 @@ async function runCodegen(opts, cwd) {
|
|
|
201
206
|
filename: sdkFile,
|
|
202
207
|
schema: schema,
|
|
203
208
|
documents,
|
|
204
|
-
config: {},
|
|
209
|
+
config: { scalars: options.scalars || {} },
|
|
205
210
|
plugins: [
|
|
206
211
|
{ typescript: {} },
|
|
207
212
|
{ 'typescript-operations': {} },
|
|
@@ -230,7 +235,9 @@ async function runCodegen(opts, cwd) {
|
|
|
230
235
|
fetcher: options.reactQuery?.fetcher || 'graphql-request',
|
|
231
236
|
legacyMode: options.reactQuery?.legacyMode || false,
|
|
232
237
|
exposeDocument: options.reactQuery?.exposeDocument || false,
|
|
233
|
-
addInfiniteQuery: options.reactQuery?.addInfiniteQuery || false
|
|
238
|
+
addInfiniteQuery: options.reactQuery?.addInfiniteQuery || false,
|
|
239
|
+
reactQueryVersion: options.reactQuery?.reactQueryVersion || 5,
|
|
240
|
+
scalars: options.scalars || {}
|
|
234
241
|
};
|
|
235
242
|
const rqContent = await (0, core_1.codegen)({
|
|
236
243
|
filename: reactQueryFile,
|
|
@@ -265,3 +272,22 @@ async function runCodegenFromJSONConfig(configPath, cwd) {
|
|
|
265
272
|
const merged = (0, options_1.mergeGraphQLCodegenOptions)(options_1.defaultGraphQLCodegenOptions, overrides);
|
|
266
273
|
return runCodegen(merged, cwd);
|
|
267
274
|
}
|
|
275
|
+
function buildTypeIndex(introspection) {
|
|
276
|
+
const byName = {};
|
|
277
|
+
const types = (introspection && introspection.__schema && introspection.__schema.types) || [];
|
|
278
|
+
for (const t of types) {
|
|
279
|
+
if (t && typeof t.name === 'string' && t.name.length > 0)
|
|
280
|
+
byName[t.name] = t;
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
byName,
|
|
284
|
+
getInputFieldType(typeName, fieldName) {
|
|
285
|
+
const typ = byName[typeName];
|
|
286
|
+
if (!typ || typ.kind !== 'INPUT_OBJECT')
|
|
287
|
+
return null;
|
|
288
|
+
const fields = typ.inputFields || [];
|
|
289
|
+
const f = fields.find((x) => x && x.name === fieldName);
|
|
290
|
+
return f ? f.type : null;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
package/esm/codegen.js
CHANGED
|
@@ -57,8 +57,8 @@ async function getIntrospectionFromEndpoint(endpoint, headers) {
|
|
|
57
57
|
const res = await client.request(q);
|
|
58
58
|
return res;
|
|
59
59
|
}
|
|
60
|
-
function generateKeyedObjFromGqlMap(gqlMap) {
|
|
61
|
-
const gen = generateGql(gqlMap);
|
|
60
|
+
function generateKeyedObjFromGqlMap(gqlMap, selection, typeNameOverrides, typeIndex) {
|
|
61
|
+
const gen = generateGql(gqlMap, selection, typeNameOverrides, typeIndex);
|
|
62
62
|
return Object.entries(gen).reduce((acc, [key, val]) => {
|
|
63
63
|
if (val?.ast)
|
|
64
64
|
acc[key] = print(val.ast);
|
|
@@ -94,11 +94,12 @@ async function writeOperationsDocuments(docs, dir, format, convention) {
|
|
|
94
94
|
await ensureDir(dir);
|
|
95
95
|
const index = [];
|
|
96
96
|
for (const key of Object.keys(docs)) {
|
|
97
|
-
const
|
|
97
|
+
const base = getFilename(key, convention);
|
|
98
|
+
const filename = base + (format === 'ts' ? '.ts' : '.gql');
|
|
98
99
|
if (format === 'ts') {
|
|
99
100
|
const code = `import gql from 'graphql-tag'\nexport const ${key} = gql\`\n${docs[key]}\n\``;
|
|
100
101
|
await writeFileUTF8(join(dir, filename), code);
|
|
101
|
-
index.push(`export * from './${
|
|
102
|
+
index.push(`export * from './${base}'`);
|
|
102
103
|
}
|
|
103
104
|
else {
|
|
104
105
|
await writeFileUTF8(join(dir, filename), docs[key]);
|
|
@@ -112,7 +113,10 @@ export async function runCodegen(opts, cwd) {
|
|
|
112
113
|
input: { ...(defaultGraphQLCodegenOptions.input), ...(opts.input || {}) },
|
|
113
114
|
output: { ...(defaultGraphQLCodegenOptions.output), ...(opts.output || {}) },
|
|
114
115
|
documents: { ...(defaultGraphQLCodegenOptions.documents), ...(opts.documents || {}) },
|
|
115
|
-
features: { ...(defaultGraphQLCodegenOptions.features), ...(opts.features || {}) }
|
|
116
|
+
features: { ...(defaultGraphQLCodegenOptions.features), ...(opts.features || {}) },
|
|
117
|
+
selection: { ...(defaultGraphQLCodegenOptions.selection), ...(opts.selection || {}) },
|
|
118
|
+
scalars: { ...(defaultGraphQLCodegenOptions.scalars || {}), ...(opts.scalars || {}) },
|
|
119
|
+
typeNameOverrides: { ...(defaultGraphQLCodegenOptions.typeNameOverrides || {}), ...(opts.typeNameOverrides || {}) }
|
|
116
120
|
};
|
|
117
121
|
const root = join(cwd, options.output.root);
|
|
118
122
|
const typesFile = join(root, options.output.typesFile);
|
|
@@ -131,8 +135,9 @@ export async function runCodegen(opts, cwd) {
|
|
|
131
135
|
const schema = hasEndpoint
|
|
132
136
|
? buildClientSchema(introspection)
|
|
133
137
|
: buildSchema(await readFileUTF8(schemaPath));
|
|
138
|
+
const typeIndex = buildTypeIndex(introspection);
|
|
134
139
|
if (options.features.emitOperations || options.features.emitSdk || options.features.emitReactQuery) {
|
|
135
|
-
docs = generateKeyedObjFromGqlMap(gqlMap);
|
|
140
|
+
docs = generateKeyedObjFromGqlMap(gqlMap, options.selection, options.typeNameOverrides, typeIndex);
|
|
136
141
|
}
|
|
137
142
|
if (options.features.emitOperations) {
|
|
138
143
|
await writeOperationsDocuments(docs, operationsDir, options.documents.format, options.documents.convention);
|
|
@@ -142,7 +147,7 @@ export async function runCodegen(opts, cwd) {
|
|
|
142
147
|
filename: typesFile,
|
|
143
148
|
schema: schema,
|
|
144
149
|
documents: [],
|
|
145
|
-
config: {},
|
|
150
|
+
config: { scalars: options.scalars || {} },
|
|
146
151
|
plugins: [{ typescript: {} }],
|
|
147
152
|
pluginMap: { typescript: typescriptPlugin }
|
|
148
153
|
});
|
|
@@ -161,7 +166,7 @@ export async function runCodegen(opts, cwd) {
|
|
|
161
166
|
filename: sdkFile,
|
|
162
167
|
schema: schema,
|
|
163
168
|
documents,
|
|
164
|
-
config: {},
|
|
169
|
+
config: { scalars: options.scalars || {} },
|
|
165
170
|
plugins: [
|
|
166
171
|
{ typescript: {} },
|
|
167
172
|
{ 'typescript-operations': {} },
|
|
@@ -190,7 +195,9 @@ export async function runCodegen(opts, cwd) {
|
|
|
190
195
|
fetcher: options.reactQuery?.fetcher || 'graphql-request',
|
|
191
196
|
legacyMode: options.reactQuery?.legacyMode || false,
|
|
192
197
|
exposeDocument: options.reactQuery?.exposeDocument || false,
|
|
193
|
-
addInfiniteQuery: options.reactQuery?.addInfiniteQuery || false
|
|
198
|
+
addInfiniteQuery: options.reactQuery?.addInfiniteQuery || false,
|
|
199
|
+
reactQueryVersion: options.reactQuery?.reactQueryVersion || 5,
|
|
200
|
+
scalars: options.scalars || {}
|
|
194
201
|
};
|
|
195
202
|
const rqContent = await runCoreCodegen({
|
|
196
203
|
filename: reactQueryFile,
|
|
@@ -225,3 +232,22 @@ export async function runCodegenFromJSONConfig(configPath, cwd) {
|
|
|
225
232
|
const merged = mergeGraphQLCodegenOptions(defaultGraphQLCodegenOptions, overrides);
|
|
226
233
|
return runCodegen(merged, cwd);
|
|
227
234
|
}
|
|
235
|
+
function buildTypeIndex(introspection) {
|
|
236
|
+
const byName = {};
|
|
237
|
+
const types = (introspection && introspection.__schema && introspection.__schema.types) || [];
|
|
238
|
+
for (const t of types) {
|
|
239
|
+
if (t && typeof t.name === 'string' && t.name.length > 0)
|
|
240
|
+
byName[t.name] = t;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
byName,
|
|
244
|
+
getInputFieldType(typeName, fieldName) {
|
|
245
|
+
const typ = byName[typeName];
|
|
246
|
+
if (!typ || typ.kind !== 'INPUT_OBJECT')
|
|
247
|
+
return null;
|
|
248
|
+
const fields = typ.inputFields || [];
|
|
249
|
+
const f = fields.find((x) => x && x.name === fieldName);
|
|
250
|
+
return f ? f.type : null;
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|